Skip to main content
Your sandbox integration and your live integration are the same code against two base URLs. Going live is mostly about hardening the webhook handler and swapping the key. Walk this checklist before your first real user.
Live base URL: https://api.ledgersyncappv2.com/v3  ·  Key prefix: sk_live_.... Sandbox keys (sk_test_...) do not authenticate against the live base URL, and vice-versa.

Before your first live user

1

Mint a live API key

Same flow as sandbox, but choose live as the environment. Copy the sk_live_... value once, it’s shown only at creation, and store it in your server’s secret store. See Authentication.
2

Store the signing secret somewhere durable

The signing_secret from your webhook subscription is shown exactly once. Persist it to a real secrets manager (GCP Secret Manager, AWS Secrets Manager, 1Password Connect, whatever you already run) before you close the tab. You need it to verify every delivery.
3

Dedupe on event_id

We retry failed deliveries with exponential backoff for 24 hours, so the same event can land more than once. Keep a short-lived set of seen event_ids and short-circuit duplicates. Your handler must be idempotent.
4

Return 2xx fast

Acknowledge each delivery within 30 seconds. If your work is slow (DB writes, notifications, downstream calls), enqueue it and return 200 immediately, anything else looks like a failure and triggers a retry.
5

Verify signatures constant-time

Compare the HMAC with hmac.compare_digest (Python) / crypto.timingSafeEqual (Node) / your language’s equivalent, never a plain ==, which leaks timing. Reject any delivery whose X-LS-Webhook-Timestamp is more than 5 minutes old. Full recipe in Webhooks.
6

Exercise your handler with webhook.test

Fire POST /v3/webhooks/subscriptions/{id}/test any time. It’s signed with the subscription’s real signing_secret and carries the same headers as production, so your verification path runs end-to-end. It bypasses the event_types filter, so you don’t need to subscribe to webhook.test for it to land.
7

Plan for connection.failed and connection.disconnected

Banks reject credentials, MFA tokens expire, users revoke access. Subscribe to both events and surface a clear re-link call to action in your UI when either fires. See Connection lifecycle.
8

Log X-LS-Trace-Id everywhere

Every response, including errors, carries an X-LS-Trace-Id. Log it. When you open a support ticket, quote it and we jump straight to your request.
The single most common go-live bug is a webhook handler that isn’t idempotent or is too slow to ack. Get dedupe-on-event_id and return-2xx-fast right in sandbox with webhook.test before you ship.

What changes from sandbox

Same endpoints, same webhook shapes, same routing. If it worked against the FinBank smoke test, it works live.

Rate limits

Every API key is rate-limited per environment. The live tier trades a lower sustained rate for a much larger daily budget and a short burst allowance; the sandbox tier lets you hammer it faster while you build but caps the daily total. Every response, success or error, carries these headers: Read X-RateLimit-Remaining and back off before you hit zero rather than waiting for the 429. Because live gets a 2× burst, a single spike can pass while the minute window is technically full, then the next request trips.

When you get throttled

Over the limit, the API returns 429 with the standard error envelope and a Retry-After header (seconds):
Honor Retry-After exactly: on a per-minute trip it’s the seconds to the next minute boundary; on a daily-budget trip it’s the seconds until the day rolls over, which can be hours.

Service-layer caps

Some limits are enforced deeper in the stack and are not reflected in the X-RateLimit-* headers. You’ll still see a 429 (rate_limit_exceeded) when you cross them:
Statement extractions are capped separately because each one burns a real OCR-provider call, even on sandbox keys. Queue extraction jobs and cap your own concurrency rather than firing them all at once.
Don’t hardcode these numbers into retry logic. Drive backoff off X-RateLimit-Remaining and Retry-After so your integration keeps working if a tier changes.