# Go live

Move from sandbox to production: production Daraja credentials, the callback URL you paste into the Daraja portal, and the go-live checklist.

## What changes

Almost nothing. Your code is the same; three things move.

1. Your application's **Production** environment gets your *production* Daraja credentials and your real shortcode.
2. You paste the **production callback URL** into the Daraja portal.
3. Your backend swaps `mp_test_…` for `mp_live_…`.

The endpoints, the request bodies, the webhook envelope and the result codes are identical. That is the whole point of building against [sandbox](/docs/guides/test-in-sandbox) first.

## 1. Get production Daraja credentials

Production credentials are issued by Safaricom against your **real** till or paybill, on the [Daraja portal](https://developer.safaricom.co.ke) under "Go Live". You will need:

- Your consumer key and secret for the production app.
- Your shortcode — the till or paybill number customers already pay.
- Your Lipa na M-Pesa Online passkey.
- For payouts, reversals, and the admin queries: an **Initiator Name** and **Initiator Password** (a.k.a. the security credential), issued separately.

Paste them into the application's **Production** environment. They are encrypted at rest and never read back.

## 2. Set the callback URL in the Daraja portal

paylod hosts the callback receiver, so what you paste into Daraja is the URL paylod shows you on the application's **Endpoints & Webhooks** tab.

> This is the one step nobody can do for you — Safaricom has no API for registering a callback URL. Everything else in a paylod integration is automatable.

Treat the callback URL as a secret. It carries a per-application token.

## 3. Swap the key

Mint an `mp_live_…` key on the **API Keys** tab and put it in your server's secret store. Your code does not change at all:

```bash title=".env"
PAYLOD_API_KEY=mp_live_YOUR_API_KEY
```

The key alone decides that this is production. There is no environment flag to forget.

## Go-live checklist

- The API key lives on your **server**, never in a browser or mobile app.
- The `amount` comes from your own order record, never from the client.
- Your webhook handler **verifies the HMAC signature** before parsing the body. `paylod.webhookHandler()` does this for you.
- You compare `data.amount` against the order total **before** fulfilling.
- One `Idempotency-Key` per logical order; retries reuse it.
- `429` is handled with a backoff, not a crash.
- Your failure paths (`1032`, `1037`, `2001`) have been exercised at least once.
- If you use payouts or admin queries, the Initiator Name and Password are set on the **Production** environment.

The reasoning behind each of these is in [Secure integration](/docs/guides/security).

## Then watch it

Payments, webhook deliveries and their retries are all visible in the dashboard. If a delivery is failing, you will see the attempts and the response your endpoint returned.
