API
One REST surface, generated from the schema
The database tables, the endpoints, this document and every page of the application come from one set of entity definitions. The useful consequence for you is that there are no endpoints that exist for one object and not another: whatever you learn about contacts is true of templates, sends and suppressions.
Before the first call
Base URL
https://crm-merge.mycompanylist.com/api/v1
Authentication
Send an API key as the X-API-Key header. A browser that has signed in carries a session cookie instead, and either is accepted. A request with neither is refused with 401 and a message telling you which two things it would have taken.
The plan catalogue lists API access against Pro and Scale -- see pricing. Keys are issued by the operator; there is no screen in the account that creates one, so ask for yours rather than going looking.
A first request
curl -H "X-API-Key: $EMAIL_APP_KEY" \ "https://crm-merge.mycompanylist.com/api/v1/contacts?limit=5&sort=-created_at"
Conventions that hold everywhere
Listing
Every collection accepts limit, offset, page (a 1-based alternative to offset), sort (a comma-separated list of fields, a leading - for descending), q (substring search across the fields the schema marks searchable), fields (a comma-separated column subset), expand (reference fields to inline), and include_deleted.
Any other query parameter is a filter on the field of that name, which is why there is no separate filter syntax to learn: ?email_status=active&opted_out=false means what it looks like. Repeat a parameter to pass several values.
The page envelope
{
"items": [ ... ],
"total": 1842,
"limit": 50,
"offset": 0,
"has_more": true
}count=false skips the counting query on a large table and returns total: null. has_more is still correct.
Anything that is not CRUD is an action
Operations that are not create, read, update or delete are declared on the entity and reached the same way everywhere: POST /api/v1/{entity}/{id}/actions/{name} for one record, POST /api/v1/{entity}/actions/{name} for the collection, and POST /api/v1/{entity}/bulk/{operation} for many at once. A record's related collections are GET /api/v1/{entity}/{id}/{relation}.
Errors say which kind of wrong it was
| Status | What it means |
|---|---|
| 400 | The value was not usable. |
| 401 | No API key and no session. |
| 403 | Signed in, and not allowed to do this. |
| 404 | No such record -- or no such entity, which is also the answer when an entity exists and is not yours to see. A 403 there would confirm it exists. |
| 405 | The entity is read-only through the API. |
| 409 | It conflicts with something already stored. |
| 422 | Validation. The body carries detail: [{loc, msg}], one entry per field. |
| 502 | A relay or CRM refused us, and its own message is passed through rather than hidden behind a 500. |
Secrets go in and never out
Credential fields accept a value and never return one, not even truncated or masked into something that looks like the original. Read a connector back and the credential is simply absent. Set one through the action the schema declares for it.
Reference
This is a curated document, not the whole API. Entities are published by name rather than excluded by name, so an entity added to the schema -- including one you add yourself at run time -- is not published here until somebody decides it should be. Your own account's full document is at /api/v1/meta/openapi.json once you are signed in.
Account
Auth
Public
- GET
/api/v1/public/openapi.jsonThe published subset of this API - GET
/api/v1/public/plansEvery plan, its price, its limits and its features - GET
/api/v1/public/signupWhether this deployment is taking new accounts - POST
/api/v1/public/support-ticketsRaise a support ticket without an account - GET
/api/v1/public/support-tickets/formA signed token the help form must submit with