Environments
Two environments. They do not share credentials, they do not share data, and the sandbox never places a real telephone call.
| Live | Sandbox | |
|---|---|---|
| API host | https://api.pacepayments.ai |
https://sandbox.api.pacepayments.ai |
| Token host | https://auth.pacepayments.ai |
https://auth.pacepayments.ai |
| Credentials | Live client registration | Separate sandbox registration |
| Telephony | Real calls, real people | Simulated - no call is placed |
simulate block |
403 simulate_not_allowed |
Accepted |
| Data | Production tenant data | Its own data space |
The one check that matters
Section titled “The one check that matters”Every event carries environment. Check it before you process a webhook.
if event["environment"] != MY_ENVIRONMENT: return 200 # acknowledge, then ignoreThis looks redundant until the day someone points a sandbox endpoint at a production handler, or copies a URL between two configuration files. When that happens, the consequence is a simulated payment promise written into a real case file - and nothing about the payload would otherwise tell you it was not real. Acknowledge the delivery so Pace does not retry it, then drop it.
GET /v1/tenant returns your own environment value, so a service can learn which
one it is in at startup rather than being told through a build flag that may be wrong.
What the sandbox actually simulates
Section titled “What the sandbox actually simulates”The sandbox does not fabricate results. It writes exactly the same records a real call writes when it hangs up, and then runs the same derivation over them that production runs. There is no second result producer.
That distinction is not academic. A simulator that assembled Result objects would
only ever test the simulator. This one tests the derivation that also runs live -
and during its first run it surfaced three defects in the live path that no unit
test had caught.
What it means for you: a result you receive in the sandbox has the same shape, the same fields and the same edge cases as a live one. Writing your handler against the sandbox is time well spent.
Everything simulated is marked simulated: true internally and its call identifier
begins with SIMUL. You will not normally see those markers - they exist so that
Pace’s own analytics stay honest.
Differences you will notice
Section titled “Differences you will notice”The first attempt lands on the next quarter-hour boundary. Dial scheduling rounds up to a fifteen-minute grid, in the sandbox as in production, so an order you submit at 09:07 will not be attempted before 09:15. Budget for it in a test script rather than assuming something is broken.
busy is not offered. It appears in the CallDisposition registry, but the live
path maps a busy signal onto no_answer and never produces the value. A sandbox that
handed you busy would invite you to write retry_on: ["busy"] - green in test,
dead in production. See Sandbox testing.
There are no test clocks. You cannot fast-forward a retry window or a wait step. Long waits have to be tested with short durations.
Keeping the two apart
Section titled “Keeping the two apart”-
Separate credentials, separate stores
Sandbox and live secrets should not live in the same vault path. A copy-paste between two entries in the same file is the failure this prevents.
-
Separate webhook endpoints
Register the sandbox URL on the sandbox host only. A single endpoint receiving both streams is one
environmentcheck away from a bad day. -
Assert the environment at startup
Read
GET /v1/tenantand compare it against what your deployment believes. Refuse to start on a mismatch - that is far cheaper than discovering it later in a case file.