API, integration and operations
A contract before the code, a production nobody touches by hand
Everything the web console does can also be done through the API, under the same authorisations. The contract is written before the implementation, published, and compared on every integration run; how the software reaches production is part of compliance.
Contract and conventions
Nothing is implemented before it is described. The contract is the source of truth for consumers.
- Everything is a resource with a prefixed opaque identifier; no sequential identifier is ever exposed.
- Cursor pagination, never offsets: a page stays stable even as the collection moves.
- Idempotent creations: an idempotency key replayed within twenty-four hours returns the original response, with no side effect.
- RFC 9457 errors, with a stable business code and a request identifier that can be found again in the audit log.
- Dates in ISO 8601, in universal time, everywhere.
- A space is never given as a parameter: it is derived from the key. A resource of another space is not found.
GET /v1/contacts/ctc_…
Authorization: Bearer rt_live_… # platform role
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
{
"type": "…/errors/not_found",
"title": "Resource not found",
"status": 404,
"code": "not_found",
"request_id": "req_3f2a…"
}
# A refusal says "not found", never
# "forbidden". It is written to the audit
# log; no read entry is, because there
# was no read.
What an idempotency key guarantees
- The same call replayed with the same key returns the original response, identically, for twenty-four hours — without creating a second resource or triggering a second send.
- The same call replayed with a different body is refused: the key commits the request, not merely the intent.
- Two concurrent calls carrying the same key produce a single effect; the second waits for the first one’s result.
- After the window, the key is forgotten: a later reuse is a new request.
Five calls to your first contact
The shortest path from a collection form to a first message on its way.
- Create a list — the entry point of contacts and the most common trigger.
- Create a template — the unsubscribe link is mandatory for marketing content; the sending domain must be verified.
- Create and activate a journey — send, wait, condition, send…
- Push the contact with its consent acts and its subscription, in a single call.
- Read its state — status, lists, enrollments, last event.
PUT /v1/contacts
Authorization: Bearer rt_live_…
Idempotency-Key: 7f1c…
{
"email": "contact@example.com",
"first_name": "Camille",
"consents": [
{ "purpose": "marketing_email", "status": "granted",
"at": "2026-09-19T14:32:00Z", "source": "form:landing",
"proof_url": "https://proofs.example.com/abc" }
],
"subscriptions": [{ "list_id": "lst_…" }]
}
→ 201 the contact enters the journey;
the first message is queued within a minute.
Sandbox
A mode where nothing leaves, to integrate without risk.
- Test keys are distinct and visibly prefixed; a test key never reaches production data.
- Messages are captured instead of delivered, and remain readable for checking.
- Bounces and complaints can be simulated, to exercise the journey branches that depend on them.
- The same consent rules apply: a sandbox that let through what production refuses would be of no use.
Webhooks, events and exports
Two ways to get what happens, and one common rule: no personal data on the way out.
- Pull: the event log is read by API call, with filters and cursor pagination.
- Push: signed HTTPS deliveries, with spaced retries and a log of attempts.
- Protection against redirects to internal addresses: a subscription’s URL is checked before every call.
- Payloads carry opaque identifiers only — never an e-mail address, a subject or a body.
- The business events your tools post come back in segments, conditions and triggers.
- Statistics and event-log exports, on demand or scheduled, as CSV or JSON, retrieved by download or pushed to a webhook.
What a delivery contains, and how it is verified
- An event identifier, a type, a date in universal time and opaque identifiers of resources — nothing else.
- A signature computed with a secret specific to the subscription, and a date in the header to reject replays.
- The secret is returned only when the subscription is created; it can be rotated without interrupting deliveries.
- Failed attempts are retried at increasing intervals, then abandoned, and each one can be inspected.
- A missed event is caught up through the event log: pull remains the source of truth.
Web console
The same platform, with screens — and exactly the same authorisations.
- Contacts, lists, segments, consents; journeys, campaigns and service messages; templates, fragments, files and public pages; statistics and exports; domains, keys and retention settings.
- The console is an API client like any other: it has no privileged access, and its navigation is built on the effective authorisation returned by the server.
- Actions are attached to the person who triggered them, in addition to the key used, through a signed header that names without authorising.
- Two-factor authentication for everyone; single sign-on through the customer’s identity provider is available.
- No screen works around a consent rule: what the server refuses, the console does not get either.
Operations and deployment
Whatever is not automated ends up being done by hand, one evening, without a trace.
- Deployment by artefact: a release is a verified build artefact — no history, no tests, no development tooling — published after a green continuous integration run and deployed as is. Rolling back means redeploying the previous release.
- Blocking continuous integration: static analysis, type checking, a regenerated and compared contract, unit, integration and space-isolation tests, secret scanning, dependency audit, a real start-up of the artefact.
- Migrations deployed, not run: a schema change is code embedded in the release and executed at deployment. Nobody opens a session on production.
- Ephemeral review environments, with a dedicated database and generated synthetic data. No production data outside production: no copy, no restore elsewhere, no extract in an artefact.
- No secret in the repositories: start-up refuses an empty value, an example value, an insufficient length or two identical secrets.