# Go-live checklist

Source: https://kirora.io/docs/api/production-checklist

Check identity, consent, channels, keys, retries and webhooks before real traffic, and hand the marketing team a data dictionary and a list of owners.

Work through this list before your integration carries real customers. Each item links
to the guide that explains it. Run the whole integration in a
[staging workspace](https://kirora.io/docs/api/staging) first: it uses the same rules and limits as live.

## Identity

- [ ] Every contact carries your own id as `externalId`, and your code identifies and
      tracks by it. See [IDs and identity](https://kirora.io/docs/api/identifiers).
- [ ] You handle 409 `CONFLICT` and 409 `ALREADY_EXISTS` from identify, rather than
      retrying them.
- [ ] An address change is sent with the contact's `externalId`, so it updates the
      contact instead of creating a second one.
- [ ] You know who merges duplicates (`mergeContacts`), and when.

## Consent

- [ ] Your code grants consent only when the contact agreed, and sends
      `consent.statement` (`basis` and `evidence`) with every grant. A grant without one
      is refused.
- [ ] Your code leaves consent flags out when it does not mean to change them: an
      omitted flag keeps its value, but `false` withdraws consent.
- [ ] Your app sets `consent.push` when the contact opts in: every push needs it,
      transactional included. A device's permission alone does not.
- [ ] Unsubscribes and consent changes made in Kirora reach your systems, through the
      `contact.unsubscribed` and `contact.consent_changed` [webhooks](https://kirora.io/docs/api/webhooks).

## Channels

- [ ] Your sending domain is verified, and has a DMARC record (`p=none` at least) if you
      send marketing email.
- [ ] The workspace's postal address is set if you send marketing email.
- [ ] For push, a project in the `production` environment has a ready app for each
      platform, and your server registers devices on every app start. See
      [Push notifications](https://kirora.io/docs/api/push).
- [ ] Every template your code sends by `email.template` is published. Sends use the
      published version, not the draft.

## Keys and retries

- [ ] Each service has its own `integration` key with only the scopes it needs. No key
      is in a browser, a mobile app or source control. See
      [Authentication](https://kirora.io/docs/api/reference/authentication).
- [ ] Keys used from fixed servers carry `allowedIps`.
- [ ] You know each key's expiry date (new keys expire after 90 days unless you chose
      otherwise) and who rotates it. Rotation can keep the old key working for up to
      7 days.
- [ ] Every `POST` you might retry sends an `Idempotency-Key`, derived from the action
      (`receipt-<order id>`) and generated once, before the first attempt. Every `POST`
      takes one; a batch takes one per item instead. See [Idempotency](https://kirora.io/docs/api/reference/idempotency).
- [ ] Retries are limited to transport failures, 429 and 5xx, honour `Retry-After`, and
      stop after a few attempts. See [Errors](https://kirora.io/docs/api/reference/errors#what-to-retry).
- [ ] Batch calls read every entry of `results`, not only the status. See
      [Batches](https://kirora.io/docs/api/batches).
- [ ] Your logs keep the `X-Request-Id` of every call that fails.

## Webhooks

- [ ] Your endpoint verifies the signature over the raw body, in constant time, and
      refuses timestamps more than five minutes away. See
      [Webhooks](https://kirora.io/docs/api/webhooks#verifying-the-signature).
- [ ] It responds 2xx within 5 seconds and does the work afterwards.
- [ ] It deduplicates on `eventId`, and accepts event types it does not know.
- [ ] The signing secret is stored as a secret, and someone knows how to rotate it.

## Hand-over to the marketing team

The people who build campaigns and journeys in the dashboard need to know what your
integration sends them. Give them a data dictionary, kept where both teams can edit it:

| Name | Kind | Type | Example | Sent by | Owner |
| --- | --- | --- | --- | --- | --- |
| `Order Completed` | Event | — | — | Checkout service | Payments team |
| `order_id` | Property of `Order Completed` | string | `"10042"` | Checkout service | Payments team |
| `total` | Property of `Order Completed` | number, major units | `125.5` | Checkout service | Payments team |
| `plan` | Contact attribute | string | `"premium"` | Billing service | Billing team |

With it, tell them:

- [ ] How contacts are identified, and what happens when someone changes their address.
- [ ] Which events and attributes are sent, and from when (`GET /v1/events/catalog` lists
      what the workspace has received).
- [ ] What your code sends itself (transactional messages) and what they control in the
      dashboard (campaigns, journeys, templates).
- [ ] Which test addresses and test contacts to use. Reserved addresses such as
      `delivered@example.com` are never delivered. See
      [Test addresses](https://kirora.io/docs/api/transactional#test-addresses).

## Owners

Write down a person, or a team, for each of these:

- [ ] The API keys, and their rotation.
- [ ] The webhook endpoint, and its alerts.
- [ ] The sending domain's DNS records.
- [ ] The workspace's admins, who create keys and invite members.
- [ ] Who contacts Kirora support, with request ids, when something breaks. See
      [Troubleshoot your integration](https://kirora.io/docs/api/troubleshooting).

## Next steps

- [Staging](https://kirora.io/docs/api/staging) — run all of this in a second workspace first
- [Troubleshoot your integration](https://kirora.io/docs/api/troubleshooting) — symptoms and the evidence to collect
- [Limits](https://kirora.io/docs/api/limits) — every size and count your traffic must fit