> For the complete documentation index, see [llms.txt](https://developers.gleantap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.gleantap.com/introduction.md).

# Introduction

Build integrations with the Gleantap v3 REST API: base URL, versioning, workrooms, pagination, idempotency, errors, and rate limits.

The **Gleantap v3 API** lets you read and write everything in your Gleantap account (contacts, events, segments, templates, campaigns, flows, conversations, pipelines, appointments, forms, pages, and more) over plain HTTPS and JSON.

{% hint style="info" %}
Still on the v2 API? Switch to **v2 (legacy)** with the version selector at the top of the page, or go to [developers.gleantap.com/v2](https://developers.gleantap.com/v2).
{% endhint %}

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Authentication</strong></td><td>Your API Secret for server integrations, OAuth 2.1 for apps acting on behalf of a user.</td><td></td></tr><tr><td><strong>API Reference</strong></td><td>Every v3 endpoint, grouped by resource, with request and response schemas.</td><td><a href="/api-reference-v3.md">API Reference (v3)</a></td></tr><tr><td><strong>Connect AI Tools (MCP)</strong></td><td>Use Gleantap from Claude, ChatGPT, Cursor, and other AI assistants.</td><td></td></tr></tbody></table>

## Base URL

```
https://api.gleantap.com/v3
```

## Your first request

Get your **API Secret** from **Settings → API** in Gleantap (see [Authentication](/authentication.md)), then:

```bash
curl https://api.gleantap.com/v3/contacts?limit=5 \
  -H "Authorization: Bearer YOUR_API_SECRET"
```

{% hint style="warning" %}
**There is no sandbox.** Every request runs against your real account, and sends reach real people. Build and test against a test contact or a dedicated test workroom.
{% endhint %}

## Versioning

* The **major version** is in the URL (`/v3`).
* The **minor version** is a date sent in the `Gleantap-Version` header (for example, `Gleantap-Version: 2026-05-27`). Omit it to get the latest. Older dates stay supported for at least 24 months.
* Adding response fields, optional parameters, and new endpoints is non-breaking. **Your client must tolerate unknown fields** in responses.

## Workrooms

An account contains one or more **workrooms** (often one per location). By default, reads span every workroom you can access. Every write lands in exactly one workroom. Narrow a request with any of:

| Method                               | Example                          |
| ------------------------------------ | -------------------------------- |
| Query parameter (preferred on `GET`) | `?workroom=Downtown`             |
| Header                               | `Gleantap-Workroom: wrm_65f1...` |
| JSON body field (`POST`/`PATCH`)     | `"workroom": "Downtown"`         |

You can pass a workroom name (case-insensitive), a `wrm_...` ID, or `all`. If a name is ambiguous, the API returns `400`; use the ID instead.

## IDs

IDs carry a type prefix so you can tell what they are at a glance: `con_` contact, `evt_` event, `seg_` segment, `tag_` tag, `loc_` location, `tpl_` template, `cam_` campaign, `flw_` flow, `cnv_` conversation, `msg_` message, `whk_` webhook, `whd_` webhook delivery, `job_` async job.

You can also address a contact by **your own ID** with the `external_id:` prefix:

```
GET /v3/contacts/external_id:user_12345
```

## Pagination

Collection endpoints use cursor pagination:

```json
{
  "object": "list",
  "data": [ ... ],
  "has_more": true,
  "next_cursor": "eyJ...",
  "prev_cursor": null
}
```

Pass `next_cursor` back as `?cursor=...` to get the next page.

## Filtering, sorting, and fields

* **Filter:** `?filter[email]=jane@example.com`, `?filter[created_at][gte]=2026-01-01T00:00:00Z`, `?filter[tags]=vip,trial`. Operators: `eq` (default), `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `contains`, `starts_with`, `exists`.
* **Sort:** `?sort=-created_at,email` (a leading `-` means descending).
* **Select fields:** `?fields=id,email,phone`.

## Idempotency

Every `POST` accepts an `Idempotency-Key` header (any unique string up to 255 characters). Retrying with the same key and body returns the original response with `Idempotent-Replayed: true`. Retrying with the same key and a different body returns `409 idempotency_conflict`.

{% hint style="warning" %}
`Idempotency-Key` is **required** on message sends and bulk operations, so a network retry can never send a message twice.
{% endhint %}

## Errors

Errors use [RFC 9457 Problem Details](https://datatracker.ietf.org/doc/html/rfc9457) (`Content-Type: application/problem+json`):

```json
{
  "type": "https://docs.gleantap.com/errors/contact_not_found",
  "title": "Contact not found",
  "status": 404,
  "code": "contact_not_found",
  "detail": "No contact with id con_01H8XK9R7TQM3V0YZWN4P5BJ2D",
  "request_id": "req_..."
}
```

Branch on `code`, not on `title` or `detail`. Unlike those, `code` is stable across versions. Include `request_id` when contacting support.

## Rate limits

Requests are rate-limited per API key and endpoint group. Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `X-RateLimit-Bucket`. On a `429`, wait as long as the `Retry-After` header says before retrying.

Need higher limits? Email <support@gleantap.com>.

## Actions that can't be undone

Some endpoints reach real people instantly or can't be reversed. Try them on a test contact or test workroom first:

* **Sends:** `POST /notifications/*`, `POST /campaigns/{id}/send_now`, `POST /conversations/{contact_id}/reply`. A test send is still a real send.
* **Going live:** publishing flows, forms, pages, or templates; scheduling campaigns; enrolling contacts in flows.
* **Deletes:** no trash, no undo.
* **One-way changes:** merging contacts; converting a segment to static.
* **Secret rotation:** rotating a webhook secret or form submission token breaks live integrations until they're updated.

## Upgrading from v2

The v2 API (`/v2/ExternalApi/*`, `X-API-KEY` / `X-SECRET-KEY` headers) is **deprecated** and will be retired on a published schedule. Its reference stays available under **v2 (legacy)** in the version selector. Build new integrations on v3.

Your existing v2 **API Secret** (the `X-SECRET-KEY` value) works as the v3 bearer token, so you don't need new credentials.

|            | v2                                                     | v3                                                  |
| ---------- | ------------------------------------------------------ | --------------------------------------------------- |
| Base URL   | `/v2/ExternalApi`                                      | `/v3`                                               |
| Auth       | `X-API-KEY` + `X-SECRET-KEY` headers, `app_id` in body | `Authorization: Bearer <API Secret>` or OAuth 2.1   |
| Workroom   | `app_id` in every request                              | Optional `?workroom=` / header / body field         |
| Style      | `POST` for reads and writes                            | RESTful `GET` / `POST` / `PATCH` / `PUT` / `DELETE` |
| Pagination | `limit` / `offset`                                     | Cursor (`next_cursor`)                              |
| Errors     | Ad hoc                                                 | RFC 9457 Problem Details with stable `code`         |

## Support

Questions, integration help, or higher limits: <support@gleantap.com>.
