Skip to content

Going live

Moving to live is a configuration change on your side and an activation on ours. The code does not change — that is the point of the sandbox running the same binaries.

Sandbox Live
Base URL https://sandbox.paysigna.com https://api.paysigna.com
Key psg_test_… psg_live_…
Webhook endpoints Registered in the test environment Register again in live
Signing secrets Test endpoint’s secret A different secret per live endpoint

Two things that catch people:

Webhook endpoints do not carry over. They belong to an environment. Register your production URL as a live endpoint and store its signing secret — it is not the same string as your test one.

Both the host and the key have to change together. A psg_test_… key against api.paysigna.com is a 403 with code environment_mismatch, and so is the reverse. The environment is baked into the key string so you can catch this before you deploy rather than after.

These are enforced, and each has its own error code so you can tell which one you are hitting:

Must be true Error if not
The account is activated merchant_not_live
KYC is complete — ID, settlement account, a description of what you sell kyc_required
A settlement destination is on file settlement_destination_missing
API access is enabled for the account api_integration_not_enabled
The channel you are using is enabled capability_disabled
Pricing is configured for your merchant and channel unpriced_combination

The last one is ours, not yours. If you see unpriced_combination on a live request, tell us — it means no fee is configured for that combination and nothing you can do in the portal will fix it.

All of the others are resolved in the merchant portal at app.paysigna.com, or by asking us.

Your integration is ready when all of these are true in the sandbox:

  • Your records update from the webhook, not from the create response.
  • A retried create with the same Idempotency-Key produces one payment, not two.
  • Your webhook handler reads the raw body, accepts any v1= element, compares in constant time, and rejects timestamps older than five minutes.
  • It deduplicates on event id and returns inside its timeout.
  • You record the highest sequence processed, and know how to backfill from it.
  • You reconcile against merchant_amount, not amount.
  • awaiting_action and pending are handled as normal outcomes, not failures.
  • manual_review_pending does not tell the customer the payment failed.
  • partner_timeout reads the transaction before deciding anything.
  • You log X-Request-Id alongside your own record of every call.
  • You alert on the PaySigna-Key-Grace-Expires header.

Scope it down. Grant the narrowest set of scopes the integration actually uses. A key that can only create and read collections cannot be used to create a payout if it leaks. See Authentication.

Allowlist your egress IPs. If you call from fixed addresses, turn this on. A leaked key then becomes unusable from anywhere else.

Separate keys per system. One key per service or per environment, not one key shared everywhere. Rotation is then a contained operation rather than a coordinated outage.

Rotate on a schedule, and watch the grace header. After a rotation, every response still authenticated by the old key carries PaySigna-Key-Grace-Expires. If that header is still appearing in your logs, some server is still holding the old credential — and if you do not notice, you will find out when all of them fail at the same instant.

Why you cannot test the live API from this page

Section titled “Why you cannot test the live API from this page”

The request console on the API reference only offers the sandbox. The live host is not in its server list — not hidden in the interface, but absent from the OpenAPI document this site loads, so there is nothing to select.

The reasoning is the same reasoning that gives publishable keys a purpose: a browser calling a secret-key endpoint means the secret key is in the browser, which is the thing publishable keys exist to prevent. A request console on a public documentation site is exactly that browser, and making it work against live would mean this page could hold a live secret key.

So the console targets sandbox.paysigna.com, where keys are test keys and no real money can move, and live traffic comes from your servers. If you want to check a live call by hand, use curl from a machine you control.

  1. Get the X-Request-Id from the response, or request_id from the error body.
  2. Check status.paysigna.com for a partner outage.
  3. Read the transaction — the response is never the only record, and a timeout is not evidence that nothing happened.
  4. Mail [email protected] with the request id and the PaySigna reference. Those two strings are the difference between a five-minute answer and a long conversation.

For anything that looks like a security issue, including a leaked key, write to [email protected] and rotate the key first.