Sandbox · synthetic data

Replikate.co developer sandbox

Build and test your integration with Replikate.co before you touch production. Same paths, request validation, signatures, response shapes and error codes. Nothing here bills, sends email, pays commissions or reaches production data.

Getting started

  1. Sign up at partners.replikate.co and create a sandbox key. It looks like rk_sandbox_…, is shown once and lasts up to 365 days (90 by default). Sandbox access needs no approval.
  2. Point your server at https://api.sandbox.replikate.co and send the key as Authorization: Bearer rk_sandbox_…. In production the same paths live on the origins in the table below; keep the origin and credential in configuration so switching is a configuration change.
  3. Generate your own Ed25519 key pair and register the public key once with PUT /v1/sandbox/settings. Signed routes verify against it, exactly as production verifies a project's registered key. Your private key never leaves you.
  4. Call GET /v1/sandbox/me to read your project slug (sbx-…), then run the quick starts. POST /v1/sandbox/reset returns your data to the seed.

Every response carries X-Replikate-Mode: sandbox, X-Replikate-Data: synthetic and X-Replikate-Financial-Effects: none. State is held in memory per partner account: a reset clears your data, a sandbox restart clears everything including your registered signing key, and other accounts never see it. Revoking a key in the portal is recorded there, but because the sandbox serves only synthetic data a revoked key keeps working here until it expires.

What the sandbox serves

AreaRoutesCredentialProduction origin
Project ingestionPOST /v1/projects/{project}/eventsEd25519 project signaturehttps://affiliate.commonquant.ai
ReferralsGET /r/{project}/{code}/{version}, POST /v1/projects/{project}/referrals/bindnone / project signaturehttps://affiliate.commonquant.ai
Discounts (your checkout)POST /v1/discount-integrations/{project}/commandssignature, scope discounts/{project}https://app.replikate.co
NewsletterPOST /v1/newsletters/publications/{publication}/subscriptions, …/tokens/check|confirm|unsubscribesandbox key (production: newsletter service key)https://app.replikate.co
Partner readsGET /v1/projects/{project}/partners/me, …/campaigns, …/statements, …/statements/{currency}sandbox key (production: partner OIDC token)https://affiliate.commonquant.ai
Sandbox onlyGET /v1/sandbox/me, PUT /v1/sandbox/settings, POST /v1/sandbox/resetsandbox keysandbox only

Errors use production's envelope: {"error":{"code","message","requestId"}}. Newsletter routes keep the newsletter service's {"error":"code"} form. JSON bodies are limited to 256 KiB (newsletter: 1 MiB).

Signing project requests

Sign the UTF-8 message v1\n<unix-seconds>\n<project>\n<exact body> with your Ed25519 private key. Send X-Replikate-Timestamp: <unix-seconds> and X-Replikate-Signature: <base64url signature, no padding>. Timestamps must be within 300 seconds. Discount commands sign discounts/<project> in place of the project. Sign the exact bytes you send; do not re-serialise the body afterwards.

# OpenSSL 3. Create your signing key once and register its public half.
openssl genpkey -algorithm ed25519 -out signing.pem
openssl pkey -in signing.pem -pubout -out signing.pub.pem
API=https://api.sandbox.replikate.co SANDBOX_KEY=rk_sandbox_...
jq -n --rawfile k signing.pub.pem '{signingPublicKey:$k}' |
  curl -sS -X PUT $API/v1/sandbox/settings -H "Authorization: Bearer $SANDBOX_KEY" -H 'Content-Type: application/json' --data-binary @-
PROJECT=$(curl -sS $API/v1/sandbox/me -H "Authorization: Bearer $SANDBOX_KEY" | jq -r .project)
sign() { # $1 = scope (project or discounts/project), $2 = body file
  ts=$(date +%s)
  printf 'v1\n%s\n%s\n' "$ts" "$1" | cat - "$2" > /tmp/msg
  sig=$(openssl pkeyutl -sign -inkey signing.pem -rawin -in /tmp/msg | openssl base64 -A | tr '+/' '-_' | tr -d '=')
  echo "-H X-Replikate-Timestamp:$ts -H X-Replikate-Signature:$sig"
}

Quick starts

1. Send a signed payment event

cat > event.json <<'JSON'
{"eventId":"3f0c3c5e-7d5f-4a8e-9a43-0d1c8c1b2a10","type":"payment.collected","data":{"provider":"airwallex","providerAccount":"acct-sandbox","paymentId":"pay_1","customerId":"cust_1","currency":"USD","collectedMinor":"3200","taxMinor":"0","eligibleMinor":"3200","settledAt":"2026-01-02T03:04:05.000Z"}}
JSON
curl -sS $API/v1/projects/$PROJECT/events -H 'Content-Type: application/json' $(sign $PROJECT event.json) --data-binary @event.json
# 202 {"state":"pending",...}; send the same body again: 200 {"state":"applied",...}

2. Referral click and bind

curl -sS -o /dev/null -w '%{redirect_url}\n' $API/r/$PROJECT/sandbox/1
# → your referralDestination (or this page)#partner_claim=TOKEN
cat > bind.json <<JSON
{"type":"referral.bind","token":"TOKEN","customerId":"cust_1","identity":{"issuer":"https://accounts.example.com","subject":"user-1"}}
JSON
curl -sS $API/v1/projects/$PROJECT/referrals/bind -H 'Content-Type: application/json' $(sign $PROJECT bind.json) --data-binary @bind.json

3. Apply a discount code at checkout

cat > reserve.json <<'JSON'
{"action":"reserve","data":{"operationId":"6a1f0d3e-2b4c-4d5e-8f60-718293a4b5c6","customerId":"cust_1","code":"SANDBOX20","productId":"sandbox-plus","priceId":"plus-monthly","period":"monthly","currency":"USD","amountMinor":1600}}
JSON
curl -sS $API/v1/discount-integrations/$PROJECT/commands -H 'Content-Type: application/json' $(sign discounts/$PROJECT reserve.json) --data-binary @reserve.json
# {"discountMinor":320,"payableMinor":1280,"state":"reserved",...}
# Then: dispatch {reservationId} → record_checkout {reservationId,checkoutId} → redeem {reservationId,checkoutId,redemptionId},
# or release {reservationId[,proof]} if the checkout is abandoned.

Amounts are integer minor units. A discount never changes your price: send what your checkout charges and charge the returned payableMinor.

4. Newsletter double opt-in

curl -sS -D - $API/v1/newsletters/publications/sandbox/subscriptions \
  -H "Authorization: Bearer $SANDBOX_KEY" -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","source":"web"}'
# The sandbox sends no email; the confirmation token is in X-Sandbox-Confirmation-Token.
curl -sS $API/v1/newsletters/publications/sandbox/tokens/confirm \
  -H "Authorization: Bearer $SANDBOX_KEY" -H 'Content-Type: application/json' -d '{"token":"TOKEN"}'

5. Partner earnings

curl -sS $API/v1/projects/$PROJECT/statements -H "Authorization: Bearer $SANDBOX_KEY"
curl -sS $API/v1/projects/$PROJECT/campaigns -H "Authorization: Bearer $SANDBOX_KEY"

Seed data

ItemValue
Campaigncode sandbox, version 1, 20% commission, 30-day attribution window, markets equities
Discount codesSANDBOX20 20% first invoice · SANDBOX5OFF USD 5.00 off every renewal (sandbox-plus, monthly) · SANDBOXDRAFT draft until you bind it · SANDBOXSPENT exhausted
Catalogueproducts sandbox-plus, sandbox-pro; periods monthly, yearly; currency USD
StatementsUSD: pending 12500, available 48000, paid 100000 · SGD: available 2400
Partner identityissuer https://sandbox.replikate.co, subject sandbox-partner (binding this identity is rejected as self_referral)

Not simulated: ledger effects of events (statements stay at the seed), payouts, administrator routes, email delivery, and provider calls. The full contract is in openapi.json.