Skip to main content

REST API

Every imatic product ships a versioned REST API so you can automate it from your own code. The conventions on this page — how you authenticate, how API keys work, how webhooks are signed, and how pagination, rate limits, and errors behave — are the same across products. What differs is the resources each product exposes, which are documented on its developer page.

Examples are illustrative

The request and response snippets below show the shape of calls — headers, auth, error envelopes — not an exact field-by-field schema. For the precise endpoints, parameters, and field names of a product, follow the links to that product's developer page.

Authentication

imatic uses two kinds of credentials, for two different callers:

  • JWT (session token) — what the web app uses after you sign in with email and password. It represents you in the browser and is short-lived. You don't manage this directly; the app does.
  • Scoped API key — what your code uses to call the v1 REST API. You create it yourself, it carries explicit scopes, and it doesn't expire with a browser session.

For server-to-server calls, always use an API key — never your password or a copied session token.

Sending an API key

Pass the key as a Bearer token in the Authorization header:

curl https://<product-host>/v1/event-types \
-H "Authorization: Bearer cal_abc123.s3cr3t_keymaterial" \
-H "Content-Type: application/json"

How API keys work

You create and manage API keys in each product's developer area — for example Calendar's Developers → API keys and Survey's Developers → API keys. A key has three properties worth understanding:

  • prefix.secret format — a key has a public prefix and a secret part, joined by a dot (for example cal_abc123.s3cr3t_keymaterial — Calendar keys use a short cal_ prefix). The prefix lets imatic identify the key in lists and logs; the secret is what proves it's yours.
  • Shown once — the full key value is displayed only at the moment you create it. After that, only the prefix is visible.
  • Scopes — each key is limited to the actions you grant it. Calendar's scopes are slots:read, bookings:read, bookings:write, and mcp (the mcp scope is what an AI agent's key needs — see MCP for AI agents). A request that exceeds a key's scope is rejected.
Treat API keys like passwords

A key's secret is shown only once, at creation. Copy it then and store it in a secret manager or environment variable — never in source control, a screenshot, or a shared doc. If a key is exposed, revoke it in the developer area and create a replacement. Revocation takes effect immediately.

Webhooks

Rather than polling for changes, register a webhook URL and imatic will POST an event to it when something happens — for example a booking is created in Calendar or a response is submitted in Survey. You manage webhook endpoints in each product's Developers → Webhooks area, where you can also send a test delivery.

A delivery looks roughly like this:

POST /your/webhook/endpoint HTTP/1.1
Content-Type: application/json
X-Imatic-Signature: t=1718900000,v1=9f86d081884c7d659a2feaa0c55ad015...

{
"event": "booking.created",
"data": { "...": "event-specific payload" }
}

Verifying the signature

Each delivery is signed with HMAC-SHA256 using your endpoint's signing secret, in a Stripe-style X-Imatic-Signature header. The header carries two fields: t, the unix-epoch seconds at send time, and v1, the hex HMAC of the string ${t}.${body} (the timestamp, a literal dot, then the exact raw request body). To verify, split the header, recompute v1 over t + "." + rawBody with your signing secret, and compare in constant time. Reject if they don't match — and optionally reject when t is older than a few minutes to guard against replay.

import crypto from "node:crypto";

function isValid(rawBody, signatureHeader, signingSecret) {
// Header shape: "t=<epoch-seconds>,v1=<hex hmac>"
const parts = Object.fromEntries(
signatureHeader.split(",").map((kv) => kv.split("=")),
);
const { t, v1 } = parts;
if (!t || !v1) return false;

// Sign the timestamped body: `${t}.${rawBody}` — NOT the raw body alone.
const expected = crypto
.createHmac("sha256", signingSecret)
.update(`${t}.${rawBody}`)
.digest("hex");

// Constant-time comparison to avoid timing attacks.
const a = Buffer.from(v1);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Exact scheme per product

The t=…,v1=… scheme above is Calendar's. Confirm header name, fields, and the signed string on each product's developer page before you ship a verifier.

Respond fast, process later

Return a 2xx quickly to acknowledge receipt, then do any slow work asynchronously. If your endpoint is slow or errors, deliveries may be retried.

Pagination

List endpoints return results in pages so a single call never has to load everything. Pass paging parameters on the request and read the paging metadata on the response to fetch the next page.

curl "https://<product-host>/v1/bookings?limit=25" \
-H "Authorization: Bearer cal_abc123.s3cr3t_keymaterial"
{
"success": true,
"data": [ { "...": "one item per result" } ],
"pagination": { "page": 1, "limit": 20, "total": 0, "pages": 0 }
}

Keep requesting the next page until there are no more results.

Rate limiting

API keys are rate limited to keep the platform fair and stable. If you exceed the limit, the API responds with HTTP 429 Too Many Requests. Back off and retry — ideally with exponential backoff and jitter — rather than hammering the endpoint.

{
"success": false,
"error": "rate_limited",
"message": "Too many requests. Please retry after a short delay."
}

Error handling

Errors use standard HTTP status codes with a JSON body that describes what went wrong. Codes you'll see most often:

  • 400 — the request was malformed or failed validation.
  • 401 — missing or invalid API key.
  • 403 — the key is valid but lacks the scope (or org access) for this action.
  • 404 — the resource doesn't exist or isn't visible to this key.
  • 409 — a conflict, such as trying to book a slot that was just taken.
  • 429 — rate limited; back off and retry.
  • 5xx — a server-side error; retry transient failures.

A typical error envelope:

{
"success": false,
"error": "validation_failed",
"message": "A human-readable explanation of the problem."
}

Resources by product

The conventions above are shared, but each product exposes its own endpoints. Start from the product's developer page for exact resources, parameters, and field names:

  • Calendar — event types, availability slots, bookings (create, reschedule, cancel), calendars, webhooks, API keys, and event links. Webhook events are booking.created, booking.cancelled, booking.rescheduled, and booking.no_show. See Calendar for developers.
  • Survey — forms, responses, response aggregation, AI form generation, and webhooks (response.created, response.flagged, response.updated, form.published, form.closed). See Survey for developers.
  • Voice Portal — agents, campaigns, and contacts, plus tools and MCP server registration. See Tools & MCP for agents.