# Go-live checklist

URL: https://submitator.com/docs/guides/go-live-checklist

> Move an integration from test mode to live without surprises, from keys and endpoints to your clients' real sites and the first real launch.

## What you'll build

The live version of an integration that works in test mode. Live data starts empty, keys and webhook endpoints are separate from test ones, and every live launch spends a launch you bought. This page walks through each change, then a first real launch to watch.

## Before you start

- **An integration that works in test mode**, from creating a project to handling webhooks.
- **Your first purchase.** Live keys come with it, and every launch on the live side uses 1 launch from your balance.

## 1. Create a live key

In the console, open Developers and create a key on the LIVE tab. Store it in your secret manager, apart from the test key, and point your production config at it.

```bash
curl https://api.submitator.com/v1/ping \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"
```

```js
const res = await fetch("https://api.submitator.com/v1/ping", {
  headers: { Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}` },
});
console.log(await res.json());
```

```python
import os
import requests

res = requests.get(
    "https://api.submitator.com/v1/ping",
    headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
)
print(res.json())
```

Response 200:

```json
{
  "object": "ping",
  "ok": true,
  "livemode": true,
  "account": { "name": "Northlight Studio", "state": "active" },
  "key": { "id": "key_3Rw8Kx2Lq9Vt5Nb1Zm7HcW", "permissions": "full" }
}
```

- [ ] `livemode` is `true` and `state` is `active`.
- [ ] Each server or job has its own key, so you can roll one without touching the others.
- [ ] Anything that only reads, such as a dashboard, has a `read_only` key. A portal that shows your client their logins needs a key with full permissions.
- [ ] No key is in a browser, an app or a repository.

## 2. Add your live webhook endpoint

Webhook endpoints belong to a mode, so the endpoints you added with the test key receive no live events. Add the live one with the live key: it gets its own secret, and its URL must be public, since live mode rejects hosts on `.test` such as the one below.

```bash
curl https://api.submitator.com/v1/webhook_endpoints \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://hooks.northlight.test/listings", "events": ["listing.*", "project.*", "report.*"]}'
```

```js
const res = await fetch("https://api.submitator.com/v1/webhook_endpoints", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://hooks.northlight.test/listings",
    events: ["listing.*", "project.*", "report.*"],
  }),
});
console.log(await res.json());
```

```python
import os
import requests

res = requests.post(
    "https://api.submitator.com/v1/webhook_endpoints",
    headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
    json={
        "url": "https://hooks.northlight.test/listings",
        "events": ["listing.*", "project.*", "report.*"],
    },
)
print(res.json())
```

- [ ] The live endpoint's `secret` is stored, and your code checks live deliveries with it.
- [ ] `POST /webhook_endpoints/{webhook_endpoint_id}/test` reaches the live endpoint and gets 2xx.
- [ ] Your code skips an event whose id it has handled, and checks `livemode` before it writes.

## 3. Switch to your clients' real data

Test projects stay in test mode: a live key gets 404 for their ids. Create each client's project again with the live key, from their real site.

- [ ] Sites are public: live mode rejects `.test`, `.example`, `.invalid`, `.localhost` and IP addresses with `not_public_url`.
- [ ] Each contact email is one your client reads, since some directories register the listing to it, and it cannot change after the launch.
- [ ] `external_id` holds your CRM's id, so your records link to the live projects.
- [ ] Tests that use `nobadge.` hosts or `/fail.png` images to force a failure run in test mode only.

## 4. Check the errors your code can get

- [ ] Your code branches on `error.code`, never on `message`, and logs `request_id`.
- [ ] Every `POST` you may send twice carries an `Idempotency-Key`, and a retry sends the same key.
- [ ] 429 waits the seconds in `Retry-After`; 500 and 503 retry with backoff.
- [ ] 402 `no_launches_left` stops your launches and tells you to buy more, instead of retrying.
- [ ] A listing or project in `action_required` reaches a person: `client_message` goes to your client when `waiting_on` is `client`.

[Error codes](https://submitator.com/docs/errors) says what each code means and how to fix it.

## 5. Launch one project and watch it

Before the first real launch, preview it: the readiness call is free and shows what the launch would spend and get.

```bash
curl https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness \
  -H "Authorization: Bearer $SUBMITATOR_API_KEY"
```

```js
const res = await fetch(
  "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness",
  { headers: { Authorization: `Bearer ${process.env.SUBMITATOR_API_KEY}` } },
);
const { ready, directories, cost } = await res.json();
console.log(ready, directories.selected, cost);
```

```python
import os
import requests

readiness = requests.get(
    "https://api.submitator.com/v1/projects/prj_4QzX1m9Lr2Vb7Nc8Tk3HwP/readiness",
    headers={"Authorization": f"Bearer {os.environ['SUBMITATOR_API_KEY']}"},
).json()
print(readiness["ready"], readiness["directories"]["selected"], readiness["cost"])
```

- [ ] `ready` is `true`, and `cost.available` covers the launch.
- [ ] After the launch, `project.launched` reaches your live endpoint.
- [ ] Listings move from `pending` over the next days, and most go live within a week.

## Check it worked

- `GET /ping` with the production key shows `livemode` `true`.
- `GET /webhook_endpoints` with the same key lists your live endpoint, `enabled` and with a recent `delivery_summary.last_http_status` of 2xx.
- Your system shows the first live project's listings, and their statuses match `progress` on the project.

## What can go wrong

| Code | Status | Fix |
| --- | --- | --- |
| [`not_found`](https://submitator.com/docs/errors#not_found) | 404 | A test id sent with a live key. Create the project again in live mode. |
| [`validation_failed`](https://submitator.com/docs/errors#validation_failed) | 422 | A test site, such as one on `.test`, fails with `not_public_url`. Use the client's real site. |
| [`no_launches_left`](https://submitator.com/docs/errors#no_launches_left) | 402 | Your live balance is 0. Buy launches in the console. |
| [`permission_denied`](https://submitator.com/docs/errors#permission_denied) | 403 | A read-only key tried to write, or to read logins. Use a key with full permissions there. |

## Next steps

- [Authentication](https://submitator.com/docs/authentication): keys, permissions, rolling and revoking.
- [Rate limits](https://submitator.com/docs/rate-limits): 3,600 requests per hour in each mode.
- [Support](https://submitator.com/docs/support): what to send when something looks wrong.
