API reference
Connect the customer data in your product to the audiences, campaigns, and journeys your team builds in maxclicks. The REST API lets you keep records current, send events, deliver prepared emails, and bring results back into your own systems.
Your team sets the brand, data model, content, and journeys in the app. Your code connects them to what each customer does. Requests use JSON over HTTPS, under the /v1 path.
Start with the outcome
| You want to… | Use |
|---|---|
| Connect your first service | API quickstart and authentication |
| Keep customer and business data current | Schemas, attributes, and records |
| Respond to a purchase, signup, or other customer action | Events and workflows |
| Build an audience from a description | Segments |
| Send a prepared email or campaign | Templates and broadcasts |
| Keep delivery preferences in sync | Suppressions and topics |
| Receive changes in your own system | Webhooks |
Schemas, attributes, templates, broadcasts, domains, senders, topics, and API keys are created in the app. Use their reference pages to see which read, update, and send operations the API supports.
Base URL
https://api.maxclicks.ai/v1
All examples use this production URL. Configure the base URL in one place in your integration.
Authentication
Create a key in your space under Settings → Developers → API keys. Store it in your server's secret manager or MAXCLICKS_API_KEY environment variable, then verify it with a read request:
curl https://api.maxclicks.ai/v1/me \
-H "Authorization: Bearer $MAXCLICKS_API_KEY"
GET /v1/me returns the key's masked identity, owner, bound space, and role. It does not change customer data. Confirm the space before making a write. A key without a space binding returns space: null and role: null; see space resolution.
Response envelope
Successful responses put the result in data. Check the HTTP status and then the operation's result: accepting a request does not always mean a message was delivered.
{
"data": {
"id": "con_BcwDvBUeSaSDILA5tHgpmU7I",
"email": "[email protected]"
}
}
Responses can also include a top-level warnings array. Log these messages: they explain adjustments such as a clamped page size or an identifier update that was skipped. List endpoints also return pagination.
Object ids
Treat maxclicks IDs as opaque, case-sensitive strings. Current IDs can include model prefixes such as con_ for a contact and tpl_ for a template. Store the full value returned by the API and send it back unchanged; do not generate or parse it yourself.
Your own identifiers have a different purpose:
| Field | Resource | Use it for |
|---|---|---|
userId | Contacts | Your product's customer or user ID |
externalId | Objects | The record's ID in your source system |
eventId | Events | Your deduplication identifier for that event |
Pagination
Most lists accept limit and offset and return pagination.hasMore. The default page size is 50, usually capped at 200; offsets above 10000 return 400 offset_too_large. Advance using the returned pagination.limit, since some endpoints apply a lower cap.
GET /v1/events uses a cursor. Pass pagination.nextCursor back as cursor, keeping the same schema and time range. See pagination for both response shapes and iteration examples.
Error envelope
Standard failures return an error object. An aborting event batch instead returns its rollback results under data with HTTP 422. Branch on the HTTP status and error.code, and use error.message for diagnostics.
{
"error": {
"type": "invalid_request_error",
"code": "identifier_conflict",
"message": "A record with this identifier already exists."
}
}
For standard errors, type is invalid_request_error on 4xx and api_error on 5xx. code can be null; validation failures may include issues. Clients should tolerate extra fields and a missing type on an unresolved-operation response. The error guide explains what to correct, retry, or reconcile.
Rate limits
Authenticated requests share three buckets per API key:
| Bucket | Limit | Applies to |
|---|---|---|
read | 100 requests/second | Read endpoints |
write | 25 requests/second | Writes outside the AI bucket |
ai | 10 requests/minute | Segment generation; webhook writes with a custom filter condition |
On 429, respect Retry-After and add jitter before retrying. Handle both HTTP dates and delay seconds. Public form endpoints have separate limits. See rate limits.
Idempotency
Use a fresh Idempotency-Key for each logical write on a supported endpoint. Keep that key and the request body when retrying the same operation. A completed result can be replayed; an unresolved result must be reconciled before you start another operation.
A timeout or 5xx does not prove that a write failed before taking effect. Do
not switch to a new key to force it through. See safe retries and unresolved
outcomes.
Write only fields your schema accepts
Record and event inputs are flat JSON objects: platform fields and custom attribute keys sit at the top level. Read attribute definitions first. A writable field has definition.type other than evaluated and no definition.valueSource. Aggregates keep a stored type and use valueSource.type: "aggregate"; sending an aggregate value does not update its source data.
Use JSON numbers for number and booleans for boolean. Send decimal as an exact string such as "42.50": writable decimals allow up to 15 integer digits and warn beyond five fractional digits. Use YYYY-MM-DD for date only, HH:mm:ss.sss for time only, and an ISO 8601 timestamp for date time. Structured fields use JSON arrays/objects. Platform timestamps are UTC ISO 8601 strings. Use the schema definition's nullable, options, formats, and JSON schema to validate inputs; omission and explicit null have different update meanings. Public record reads return stored data and platform fields. They have no evaluated/aggregate expansion flag and no ad-hoc record search query.
Accept an event, then check readiness
Send a business occurrence to an event schema with a stable eventId. Explicitly link contactId or objectId when the occurrence belongs to an existing subject; only one may be supplied. An email inside custom event data does not create that subject link.
curl -X POST https://api.maxclicks.ai/v1/events/purchase-completed \
-H "Authorization: Bearer $MAXCLICKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"eventId":"order-1042-paid","contactId":"YOUR_CONTACT_ID","amount":42}'
Replace the schema, contact, and custom fields with your configured values. A 202 response gives an immutable receipt:
{
"data": {
"accepted": 1,
"id": "ev_BcwDvBUeSaSDILA5tHgpmU7I",
"eventId": "order-1042-paid",
"isDuplicate": false,
"readinessAtAcceptance": "pending"
}
}
Missing AI auto-fill values can require asynchronous preparation. Save the receipt, then call GET /v1/events/{schema}/{id}/status with the maxclicks id. readinessState can be pending, ready, failed, or outcome_unknown. Ready means prepared for consumers; it does not confirm completion of a workflow or email. Failed or unresolved preparation needs investigation; a new eventId would create another occurrence.
Retry a lost acceptance with the same eventId, normalized payload, and explicit occurredAt if provided. The same occurrence returns its original receipt with isDuplicate: true; its original readinessAtAcceptance does not change when preparation finishes. Changed input conflicts. Normalized inputs are capped at 256 KiB, and an explicit occurredAt must be within the previous 366 days or five minutes into the future.
For batches, onError: "continue" commits each accepted item independently and returns per-item failures, even with HTTP 200. onError: "abort" rolls back new acceptances on the first failure, marks earlier items rolled_back, and returns HTTP 422 with a data result. Check every item before advancing your source checkpoint. Admission limits apply per occurrence.
Browser event collection has a separate configured source and track model. To bind an anonymous source identity to a contact, use event identity changes from a server. All six fields are required; keep the per-alias revision as a string and reuse the UUID operation ID on retries. Do not send your public API key from a browser.
Read the operation's outcome
| Action | What the immediate result establishes | What to inspect next |
|---|---|---|
| Fire an event | Durable acceptance and its original readiness | Current readiness, then downstream runs |
| Trigger a workflow | A run was created; data is an empty object | Workflow runs and history |
| Send a broadcast | Scheduling was accepted | Broadcast runs; the app exposes fuller planning/quality state |
| Send a template | Synchronous send result, possibly status: "failed" on HTTP 200 | Result status/error, then delivery events |
| Delete a record | Deletion operation ID and current state | Deletion status until completed |
Record deletion can return HTTP 200 with status: "pending". Keep the entire operationId; pending means cleanup is unfinished, and blocked means unresolved work requires reconciliation. Do not treat an HTTP success as completed erasure.
Versioning
The OpenAPI document describes API version 1.1.0, served under /v1. Use the same specification for request validation, generated clients, and API exploration: