Public REST API
Push leads in, read them back, add notes and tasks. JSON over HTTPS, token authenticated, one workspace per token.
Basics
- Base path:
/api/v1on your app host (https://app.setusuite.com). - Requests and responses are JSON. The OpenAPI 3.1 file is served at
/api/v1/openapi.yaml. - A token can do only what its scopes allow and what the role of the user who created it allows, whichever is less.
- Workspaces that are suspended, cancelled or past due lose API access as well.
Tokens and authentication
An admin with the API tokens permission creates tokens in the app (Integrations, API tokens). Choose a name, a class, at least one scope and an optional expiry of 1 to 730 days. The plain token starts with gnd_live_ and is shown once; Gandiv stores only a hash. A workspace can hold at most 25 active tokens. Tokens can be revoked at any time.
Send the token as a bearer header. The X-Api-Key header is also accepted.
Authorization: Bearer YOUR_API_TOKENScopes: leads:read, leads:write, activities:read, activities:write, tasks:read, tasks:write. A missing scope returns 403 INSUFFICIENT_SCOPE; a bad, revoked or expired token returns 401.
curl https://app.setusuite.com/api/v1/me \
-H "Authorization: Bearer YOUR_API_TOKEN"Endpoints
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /api/v1/me | any token | Show the token name, class, scopes and hourly limit. |
| POST | /api/v1/leads | leads:write | Create a lead. |
| PATCH | /api/v1/leads/{id} | leads:write | Change fields on a lead. |
| GET | /api/v1/leads/{id} | leads:read | Fetch one lead. |
| GET | /api/v1/leads | leads:read | List or search leads (q, phone, email, page, size). |
| POST | /api/v1/leads/search | leads:read | Search leads with a JSON body (q, mode, filter, sort, viewId, system, page, size, counts). |
| GET | /api/v1/leads/{id}/activities | activities:read | Read a lead's timeline (types, page, size). |
| POST | /api/v1/leads/{id}/activities | activities:write | Add a note to a lead's timeline. |
| POST | /api/v1/tasks | tasks:write | Create a task. |
| POST | /api/v1/tasks/{id}/complete | tasks:write | Mark a task done. |
| GET | /api/v1/tasks | tasks:read | List tasks (scope, status, priority, assignee, leadId, dueFrom, dueTo, page, size). |
| GET | /api/v1/requests/{id} | any token | Poll the result of an asynchronous write. |
| GET | /api/v1/openapi.yaml | none | The OpenAPI 3.1 description of API v1. |
Page size is 1 to 100. Pages start at 0.
Create a lead
Allowed fields: name, phone, altPhone, email, rating (whole number 1 to 5), statusId, source, campaign, assigneeId, fields (your workspace's custom fields by API name) and allowDuplicate. Send at least one of name, phone or email. A duplicate is refused with 409 DUPLICATE_LEAD unless allowDuplicate is true. The lead is stored with the source channel API.
curl -X POST https://app.setusuite.com/api/v1/leads \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: web-form-10231" \
-d '{
"name": "Asha Verma",
"phone": "+919800000000",
"email": "asha@example.com",
"rating": 4,
"source": "Website",
"campaign": "Diwali offer"
}'A successful create answers 201 with the created lead record (including its id). With an ASYNC token it answers 202 instead; see below.
Idempotency and token classes
Write calls (create or update lead, add note, create or complete task) accept an Idempotency-Key header of 1 to 255 visible ASCII characters. The key is scoped to the token. Repeating the same key with the same body returns the stored answer with the header Idempotent-Replay: true. The same key with a different body returns 422 IDEMPOTENCY_KEY_REUSED. If the first call is still running you get 409 REQUEST_IN_PROGRESS.
A token is either SYNC or ASYNC. SYNC tokens get the result in the response. ASYNC tokens get 202 with a requestId and a statusUrl; poll GET /api/v1/requests/{id}. Request records are kept for 2 days.
Errors and rate limits
Validation is strict. Unknown fields are rejected, not ignored. All problems are returned at once with HTTP 422:
{
"code": "VALIDATION_FAILED",
"message": "2 fields are invalid",
"errors": [
{ "field": "phone", "code": "INVALID_PHONE", "message": "'123' is not a valid phone number. ..." },
{ "field": "colour", "code": "UNKNOWN_FIELD", "message": "'colour' is not a recognised field. ..." }
]
}Unreadable JSON returns 400 INVALID_JSON; a malformed id or parameter returns 400 INVALID_PARAMETER.
Limits are per token per hour: 3,600 requests for SYNC tokens and 18,000 for ASYNC tokens. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Over the limit you get 429 RATE_LIMITED with Retry-After. Separately, an address with more than 20 failed authentications in a minute is blocked for a while.
Outbound webhooks
Webhooks are managed in the app, not through API v1 (there are no webhook endpoints under /api/v1). A subscription has a URL, a signing secret and a list of events; you can send a test ping, rotate the secret, view the delivery log and redeliver a failed delivery.
Events: lead.created, lead.updated, lead.status_changed, lead.assigned, lead.recaptured, lead.merged, note.added, task.created, task.completed, payment.received, call.logged. Wildcards such as lead.* or * are accepted. An empty list means all events.
Each delivery is a JSON envelope with id, type, createdAt and data (lead and activity). The id stays the same on retries. Headers: X-Gandiv-Event, X-Gandiv-Delivery and X-Gandiv-Signature.
X-Gandiv-Signature: t=<unix seconds>,v1=<hex>
v1 = HMAC-SHA256(signing secret, "<t>.<raw body>")Check the signature and reject old timestamps to block replays. A failed delivery is retried with backoff (about 30 seconds, 1, 2 and 4 minutes). After 5 failed attempts it is marked dead and can be redelivered by hand. After 20 dead deliveries in a row the subscription is switched off. Webhook URLs that point at private addresses are refused.
Not supported
- API v1 has no endpoint to delete a lead, or to create and manage webhooks and tokens. Those are done in the app.
- Notes are the only activity type that can be added through the API.
- No official client libraries are published. Use plain HTTP or generate a client from the OpenAPI file.
See Gandiv on your own leads
Start free, or book a short demo and we will set it up with your pipeline.