Skip to content

Developers

REST API and webhooks

Read and write every record in your workspace from your own scripts and tools, and get notified when things change. The API and webhooks are available on Starter, Pro and Business.
On this page

Authentication

Create a key in Settings → API & webhooks. Keys start with grw_ and are shown once, so store yours somewhere safe. A key acts as the user who created it, with that user's role, in that workspace. Revoke it from the same page.

Send the key as a bearer token. The base URL is your own Growlyne address:

curl https://crm.yourcompany.com/api/v1/me \
  -H "Authorization: Bearer grw_your_key"

Read-only users

Keys of viewers can read but not write: any write returns 403.

Endpoints

:object is an object key: contacts, companies, deals, tickets, or the key of a custom object. GET /api/v1/objects lists them all with their property keys.

MethodPathDoes
GET/api/v1/meThe key's user, role and workspace
GET/api/v1/objectsObjects, their properties and pipelines, and the workspace users
GET/api/v1/records/:objectList records. Query: limit, offset, search, filters (JSON), sort=prop:asc|desc, listId
GET/api/v1/records/:object/:idOne record with its associations, tasks and emails
POST/api/v1/records/:objectCreate a record: { values, associate? }
PATCH/api/v1/records/:object/:idUpdate a record: { values }
DELETE/api/v1/records/:object/:idArchive a record
POST/api/v1/records/:object/:id/notesAdd a note to the timeline: { body, subject? }
GET/api/v1/tasksYour open tasks
POST/api/v1/opsRun any operation the app uses: { op, args }

Examples

Create a contact

curl -X POST https://crm.yourcompany.com/api/v1/records/contacts \
  -H "Authorization: Bearer grw_your_key" \
  -H "content-type: application/json" \
  -d '{ "values": { "email": "sam@example.com", "firstname": "Sam" } }'

Search deals, newest first

curl "https://crm.yourcompany.com/api/v1/records/deals?search=renewal&sort=createdAt:desc&limit=50" \
  -H "Authorization: Bearer grw_your_key"

Add a note to a record

curl -X POST https://crm.yourcompany.com/api/v1/records/contacts/REC_ID/notes \
  -H "Authorization: Bearer grw_your_key" \
  -H "content-type: application/json" \
  -d '{ "subject": "Call", "body": "Discussed the Q1 rollout." }'

Run an operation

POST /api/v1/ops exposes the operations the app itself uses, such as record.merge, record.associate, record.bulkUpdate or task.upsert. An unknown name returns 400 with the list of available operations.

curl -X POST https://crm.yourcompany.com/api/v1/ops \
  -H "Authorization: Bearer grw_your_key" \
  -H "content-type: application/json" \
  -d '{ "op": "record.merge", "args": { … } }'

Property keys depend on your workspace: read them from GET /api/v1/objects before writing.

Responses, errors and limits

Successful responses are JSON in the shape { "data": … }. Errors are { "error": "message" } with a matching status:

StatusMeaning
400Invalid input, or an unknown operation
401Missing or invalid key
402The workspace plan does not include the API
403Your role does not allow this
404No such route or record
429Rate limit reached

The rate limit is 600 requests per minute per user.

Webhooks

Workspace admins add webhooks in Settings → API & webhooks: an HTTPS URL and the events to receive. Workflows can also call a webhook as one of their actions.

Events :

EventWhen
record.createdA contact, company, deal, ticket or custom record is created (app, API, form, import, inbox)
record.updatedA property changes. The body has the record and the list of changes
record.deletedA record is deleted for good
deal.stage_changedA deal moves to another stage, with the previous and new stage ids
form.submittedSomeone submits one of your forms

Each delivery is a POST with a JSON body { "event", "data", "sentAt" } and these headers:

HeaderValue
growlyne-eventThe event name
growlyne-deliveryA unique id for this delivery, handy to ignore repeats
growlyne-signatureHex HMAC-SHA256 of the raw body, keyed with the webhook's secret

Verify the signature before trusting a payload:

import { createHmac, timingSafeEqual } from "node:crypto";

const expected = createHmac("sha256", process.env.GROWLYNE_WEBHOOK_SECRET)
  .update(rawBody)
  .digest("hex");
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["growlyne-signature"]));

Answer with any 2xx within 10 seconds. A failed delivery (network error, timeout or another status) is retried up to 5 times with growing delays, and recent deliveries are listed in settings with their status.