Most integration problems that show up months later were decided, badly, in the first conversation — usually without anyone noticing a decision was made. Nobody wrote down which system owned the customer’s email address, so both systems now think they do, and update it in opposite directions when a support agent forgets which screen they’re on.
An API integration brief — the data contract, as I call it on the service page — is the short document that heads this off. It’s written before development starts, and it answers one plain question: what, exactly, are we agreeing to build?
If the implementation route is still open, I compare buying, integrating and building an internal tool before writing the brief.
What should an API integration brief include?
It should include a source of truth for every field that moves, the direction and frequency of that movement, an idempotency key that defines a duplicate, an owner for authorisation and errors, and acceptance criteria specific enough to check without a conversation. That’s the whole list.
This template is designed to put those decisions in one or two pages for agreement before development starts.
The goal of the process, and the systems involved
The brief exists to answer a narrower question than “connect system A to system B.” Two or more systems rarely need everything the other has; usually one system needs a handful of fields from the other, on a trigger, in one direction, most of the time.
So the first section of the brief just names the systems and the task in plain language: which two (or more) systems are involved, what triggers the exchange, and what “done” looks like for one instance of it. A customer signs up, a webhook fires, a support agent needs to see delivery status two systems away — whatever the actual trigger is. This section is deliberately short. Its job is to stop a request that will keep quietly growing into a general “connect everything to everything” project, which is where cost and duplication both come from.
For the hypothetical CRM-to-invoicing case used below, filled in: Systems: a CRM and an invoicing tool. Trigger: an invoice is issued, or a billing address changes. Done: the other system holds the new value once.
Source of truth and fields
For each field that will move between the systems, the brief states who owns it. Ownership isn’t the same as which system happens to be easier to read from — it’s which system’s value is correct when the two disagree.
Hypothetical example: suppose a CRM and an invoicing tool both hold a customer’s billing address. If the CRM is where a sales rep corrects an address after a call, the CRM owns that field, even though the invoicing tool is the one that needs it to print an invoice. Writing “CRM owns billing address, invoicing tool receives it” in the brief settles, in one line, an argument that would otherwise surface the first time the two disagree.
Each field also gets a direction (which system writes, which one reads) and a cadence (real-time on an event, polled on a schedule, or batched overnight). A two-way sync is nearly always several one-way flows sharing a pipe, and naming them separately is what keeps a well-meaning “sync everything” request from turning into two systems overwriting each other.
Here is a filled example for the CRM-to-invoicing case above:
Comparison table — scroll horizontally to see all columns
| Field | Owner (source of truth) | Direction | Cadence | Idempotency key |
|---|---|---|---|---|
| Customer billing address | CRM | CRM → invoicing tool | On change (webhook) | Customer ID + address version |
| Invoice total and line items | Invoicing tool | Invoicing tool → CRM | On issue (webhook) | Invoice ID |
| Payment status | Invoicing tool | Invoicing tool → CRM | Every 30 min (polled) | Invoice ID + status timestamp |
The idempotency key is the part most drafts skip, and it’s the one that prevents the most common failure: the same event arriving twice and being processed twice. It doesn’t have to be complicated — an invoice ID is often enough — but it has to be written down before development starts, not discovered after a duplicate invoice shows up in someone’s inbox.
Authorisation, errors and responsibility
The brief also has to say what happens when something goes wrong, because an integration that only works on the happy path isn’t finished.
Authorisation covers who owns the credentials on each side, where they’re stored, and who can revoke them if the integration needs to be turned off in a hurry. Errors cover three separate questions: what counts as a retryable failure versus a permanent one, where a run goes when it exhausts its retries (a dead-letter path someone actually checks, not a log line nobody reads), and who is notified when that happens. “An email to the developer” is a valid answer as long as it’s written down; the failure here is usually that nobody decided, not that the wrong person was picked.
For the CRM-to-invoicing case, filled in: Credentials — one API key per side, held by the operations lead in the company password manager; either side can revoke. Retryable — timeouts and 5xx responses, retried up to the agreed limit; permanent — a 4xx for an unknown customer ID. Dead-letter path — a failed-runs table in the integration’s own database. Notified — the operations lead, by email, on every dead-lettered run.
One concrete, publicly documented case worth naming: Shopify’s own webhook documentation states plainly that delivery “isn’t always guaranteed,” and recommends two things a consumer should do about it — ignore duplicate deliveries by checking the X-Shopify-Webhook-Id header on each event, and run a periodic reconciliation job that fetches data from Shopify’s API so the receiving system doesn’t depend on webhooks alone. Neither of those is optional detail; they’re the reason the idempotency key and the dead-letter path exist in the first place, for this API and for most others that fire webhooks.
Acceptance criteria
The last thing the brief settles is what “finished” means, in terms a third party could check without asking what you meant. A short example, again for the CRM-to-invoicing case:
- Every invoice issued in the invoicing tool appears against the matching customer in the CRM within the agreed cadence window.
- A webhook delivered twice for the same invoice ID produces exactly one record, not two.
- A billing-address change made in the CRM is reflected in the next invoice the invoicing tool issues for that customer.
- A failed delivery that exhausts its retries appears in the dead-letter log with the invoice ID and reaches the named person by alert, not only by log entry.
- Rolling the integration back (disabling the webhook, reverting to the manual process) takes one documented step and no code change.
Each item names a fact you can go and check — a count, a record, a log entry — rather than a feeling that the pipeline “works correctly.”
For an executed duplicate, wrong-order, timeout and recovery matrix, see integration retry acceptance tests.
What this brief does not decide
The brief is deliberately narrow. It doesn’t set price or timeline; those follow once the scope above is concrete enough to quote.
It doesn’t decide which of several integration approaches to use (webhook versus polling versus a scheduled export) beyond what’s already implied by the cadence row in the table — that choice usually depends on constraints the brief doesn’t capture, like rate limits or an existing job scheduler. And it says nothing about your tracking or analytics setup: a CRM-to-invoicing sync and a GA4 purchase event are unrelated questions, and this document doesn’t touch the second one.
A filled example and a blank template
The table and acceptance list above are one filled example for two hypothetical systems, a CRM and an invoicing tool. Nothing in it comes from a client’s implementation; the only real platform behaviour mentioned in this guide is Shopify’s, and only as its public documentation describes it.
The blank template (Markdown download) has the same sections and the same table, with the rows emptied, so you can fill it in for your own systems before talking to anyone about building it. If you want to draft it together instead, that’s what the API integration and automation work covers, alongside the full services list; a short note on how I approach this kind of work is on the about page.
If you’d rather talk it through first, get in touch with the systems involved and what isn’t working today.