Platinum-Edge API Website Get API keys

API documentation

Integrate deposits into your own cashier across two rails from a single API: Europe open banking (SEPA / Instant / Revolut / FPS) and LATAM (Peru, Chile, Ecuador, Mexico, Colombia). Create a payment, send the customer to the returned URL, then track its status via webhooks (recommended) or polling. Built for casino and high-volume merchants.

Base URL

https://platinum-edge.ca/api/v1

Authentication

Every request is authenticated with a secret API key you generate in the dashboard (API keys panel). Send it as a Bearer token. Keep it server-side only - never expose it in a browser or mobile app.

Authorization: Bearer pe_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Every key shown in these docs is a placeholder for illustration only - no key on this page is live and none can be issued here. Real keys are generated in your merchant dashboard. Treat them like passwords: if one leaks, revoke it in the dashboard and create a new one.

Test mode (sandbox)

Generate a separate pe_test_ key in the dashboard to integrate end-to-end with no real money. A test key uses the exact same endpoints; the platform simulates the payment instead of calling the bank, so you can build and verify your create → webhook → return_url flow before going live.

Authorization: Bearer pe_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Solution picker

Pick a country, then a method. We jump you to that solution's section with its required fields, currency, allowed document types, and a copy-paste POST /payments example.

Find your solution

Two ways to integrate (Europe)

MethodBest forYou build
Hosted Checkout (recommended)Fastest path - a casino cashier that wants minimal code and a ready-made, PCI/PSD2-grade payment page.One API call + redirect the player. We host the payment page.
Direct API (advanced)Full control over the deposit UI on your own page.Collect the player + bank details yourself, then call the API and redirect to the returned bank URL.

Both settle through the same pipeline and fire the same webhooks. Pick one per integration; you can mix per transaction. LATAM solutions always use the Direct API shape documented in Solution picker - one POST /payments that returns a redirect URL.

Europe - Open Banking SEPA / Instant / Revolut / FPS

Four open-banking methods across NL, MT and GB. Each is a standalone solution below; all share the Hosted Checkout and Direct API request shapes that follow. The per-solution blocks are generated from the same rail config the gateway uses, so they never drift.

Hosted Checkout (Europe)

Create a checkout session, then redirect the player to the returned URL. The player completes payment on a PlatinumEdge-hosted page - you never handle bank details. You can pass everything up front (player skips straight to the bank) or pass only the amount (our page collects the rest).

POST/checkout-sessions

FieldRequiredDescription
amountyesMajor units, e.g. "250.00"
return_urlrecommendedhttps:// page to send the player back to. We append ?ref=&status=.
merchant_referencenoYour player/deposit id; echoed on the webhook and status lookup.
method, bankCountrynoPre-select the rail (e.g. fps/GB). Omit to let the player choose on our page.
email, firstName, lastName, country, address1, city, zipCode, phonenoPrefill the player's details. Anything omitted is collected on the hosted page.
expires_innoSession lifetime in seconds (300-86400, default 3600).
curl -X POST https://platinum-edge.ca/api/v1/checkout-sessions \
  -H "Authorization: Bearer pe_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "250.00",
    "merchant_reference": "player-90431/dep-2207",
    "return_url": "https://cashier.example.com/deposit/return"
  }'

Response 201

{
  "ok": true,
  "id": "cs_xxxxxxxxxxxxxxxx",
  "url": "https://checkout.platinum-edge.ca/c/cs_xxxxxxxxxxxxxxxx",
  "expires_in": 3600
}

Redirect the player's browser to url. When they finish, we return them to your return_url and - separately - notify your server via webhook. Treat the webhook as the source of truth for crediting; the browser return is only a UX signal. The amount and merchant are locked to the session, so the player cannot change them.

Direct API (Europe)

POST/payments

Collect the player + bank details yourself, then create a payment and get a bank redirect URL to send your customer to.

Request body (JSON)

FieldRequiredDescription
amountyesMajor units, e.g. "250.00"
methodyessepa | instant | revolut | fps
bankCountryyesNL | MT | GB (FPS is GB only)
emailyesCustomer email
firstName, lastNameyesCustomer name
countryyesBilling country, ISO-2 (e.g. GB)
address1, city, zipCodeyesBilling address
phonenoCustomer phone
merchant_referencenoYour own ID for this deposit (player / cashier reference). Echoed back on the status webhook and on GET /payments/{reference}, so you can reconcile against your cashier without storing our reference. Max 128 chars.
return_urlnoWhere to send the player's browser after the bank flow finishes. Must be https://. We redirect to {return_url}?ref={reference}&status={status}. If omitted, the player lands on our generic result page.
idempotency_keynoA unique string per deposit attempt. If you retry the same key, you get the original payment back instead of a duplicate. Also accepted as the Idempotency-Key header.

Example

curl -X POST https://platinum-edge.ca/api/v1/payments \
  -H "Authorization: Bearer pe_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: dep_8f3a1c9e" \
  -d '{
    "amount": "250.00",
    "method": "fps",
    "bankCountry": "GB",
    "email": "customer@example.com",
    "firstName": "Alex", "lastName": "Doe",
    "country": "GB", "address1": "1 High St", "city": "London", "zipCode": "EC1A 1BB",
    "merchant_reference": "player-90431/dep-2207",
    "return_url": "https://cashier.example.com/deposit/return"
  }'

Response 201

{
  "ok": true,
  "payment": {
    "reference": "PE-MC-XXXX-XXXX",
    "redirect_url": "https://",
    "status": "redirected",
    "merchant_reference": "player-90431/dep-2207"
  }
}

Redirect the customer's browser to redirect_url to complete the bank payment. When they finish, we return them to your return_url (if supplied) and - separately - notify your server via webhook (below). Treat the webhook as the source of truth for crediting a deposit; the browser return is only a UX signal.

LATAM Peru / Chile / Ecuador / Mexico / Colombia

LATAM solutions are created with the same POST /payments endpoint by selecting a country and (where the country has more than one option) a method. The customer is sent to a hosted payment link (returned as redirect_url), and settlement is confirmed by webhook. Required fields, currency and allowed document types differ per solution - the blocks below are generated from the rail's FIELD_MATRIX + GEO_CURRENCY so the docs never drift.

Common LATAM request shape

FieldRequiredDescription
countryyesECUADOR | CHILE | PERU | MEXICO | COLOMBIA - selects the LATAM rail.
methodcond.Chile: cards | bank. Peru: bank | qr. Single-method countries (Ecuador, Mexico, Colombia) omit it.
amountyesMajor units. Currency is implied by the country (see each solution). CLP and COP are zero-decimal - send integer amounts (e.g. "5000", not "5000.00").
currencynoOptional override; defaults to the country currency.
emailyesCustomer email.
firstName, lastNameyesCustomer name (first / last).
documentTypeyesCase-sensitive ID type - allowed values are per-country (see each solution).
documentNumberyesID document number. Format is validated per geo (e.g. Peru DNI = 8 digits, Chile RUT = NNNNNNNN-D).
phone, phoneCodecond.Phone + country code (e.g. "+57"). Required for Chile cards and Colombia; Ecuador rejects phone fields entirely - omit them.
successUrl, errorUrlcond.Redirect URLs after the hosted payment. Required varies per solution.
redirectUrlcond.Colombia only - the post-interaction return URL (paid, unpaid or aborted).
expiresAtcond.Link expiry, ISO 8601. Ecuador requires a near-future expiry; Peru/Chile ignore it (provider sets 30 min).
merchant_reference, return_url, idempotency_keynoSame as the Europe Direct API - your deposit id, browser return, and idempotency.
LATAM settlement is webhook + polling. The provider only notifies on PAID / REVERTED (there is no failure webhook), so always treat the webhook as the source of truth and poll GET /payments/{reference} if a customer does not return.

Payment statuses

StatusMeaning
redirected / pendingAwaiting the customer's bank payment
approved_for_paymentAuthorized - money has left the payer's account
approvedSettled - funds received
declinedNot completed - no money taken

Webhooks

Configure a webhook URL in the dashboard (Developers section). On every status change we send a POST to that URL so your cashier can credit a deposit automatically - no polling needed. This is identical for Europe and LATAM solutions.

Events

EventWhen
payment.approvedFunds confirmed - safe to credit the player.
payment.declinedPayment failed or was abandoned - do not credit.

Payload

POST https://your-cashier.example.com/webhooks/platinum-edge
Content-Type: application/json
X-PE-Signature: t=1733600000,v1=9b2c...e1

{
  "id": "evt_4a8c...",
  "event": "payment.approved",
  "livemode": true,
  "created_at": "2026-06-08T12:00:00Z",
  "data": {
    "reference": "PE-MC-XXXX-XXXX",
    "merchant_reference": "player-90431/dep-2207",
    "status": "approved",
    "amount": "250.00",
    "currency": "GBP"
  }
}

livemode is true for real payments and false for sandbox/test payments (test API key or the dashboard "send test payment"). Use it to ignore test events in your production handler, and to drive your own test assertions.

Verifying the signature

Each delivery carries an X-PE-Signature header: t=<unix-timestamp>,v1=<hmac>. Compute HMAC-SHA256 over the string "{t}.{raw_request_body}" using your webhook signing secret (shown once in the dashboard), and compare it - in constant time - to v1. Reject deliveries older than ~5 minutes to prevent replay.

// Node.js
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const expected = crypto.createHmac("sha256", secret)
    .update(parts.t + "." + rawBody).digest("hex");
  const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
  const fresh = Math.abs(Date.now()/1000 - Number(parts.t)) < 300;
  return ok && fresh;
}

Responding & retries

Return 2xx within 10 seconds to acknowledge. Any other response (or a timeout) is retried with exponential backoff for up to 24 hours. Make your handler idempotent - key on data.reference - because a delivery can arrive more than once.

Until your webhook is configured you can still poll GET /payments/{reference} after redirecting the customer. Webhooks are strongly recommended for production deposits.

Crediting the player's balance (your side)

This is the step that tops up the player's wallet in your system. We confirm the deposit and tell you who (merchant_reference) and how much (amount + currency); your server applies it to the player. We never touch your player accounts.

Flow

  1. When you create the payment / checkout session, set merchant_reference to something that identifies the player (and ideally the deposit), e.g. "player-90431/dep-2207".
  2. On success we POST payment.approved with that same merchant_reference plus amount, currency, and our reference.
  3. Your webhook handler verifies the signature, maps merchant_reference to the player, and credits the amount - once.

Reference handler

// POST /webhooks/platinum-edge  (your server)
export default async function handler(req, res) {
  const raw = req.rawBody;                       // the exact bytes we sent
  if (!verify(raw, req.headers["x-pe-signature"], WEBHOOK_SECRET))
    return res.status(401).end();                // reject forged / stale calls

  const evt = JSON.parse(raw);
  if (evt.event === "payment.approved") {
    const playerId = playerFrom(evt.data.merchant_reference);   // your mapping
    // Idempotent: only credit the first time we see this reference.
    if (await ledger.recordOnce(evt.data.reference)) {
      await wallet.credit(playerId, evt.data.amount, evt.data.currency);
    }
  }
  return res.status(200).json({ ok: true });     // 2xx within 10s
}
Rules: verify the signature before trusting any event; be idempotent on data.reference (retries can deliver twice); the amount/currency come from us and are locked to the deposit, so a player cannot inflate them; do not credit on the browser return_url alone - it is only a UX signal. The webhook is the source of truth.

Who does what

StepPlatinumEdgeYou (casino)
Detect the bank payment succeededyes-
Send signed payment.approved (player ref + amount)yes-
Retry until acknowledged (up to 24h)yes-
Map reference to player and add balance-yes
Show the new balance in the player's profile-yes

GET/payments/{reference}

Fetch a single payment's current status.

curl https://platinum-edge.ca/api/v1/payments/PE-MC-XXXX-XXXX \
  -H "Authorization: Bearer pe_live_..."
{
  "ok": true,
  "payment": {
    "reference": "PE-MC-XXXX-XXXX",
    "status": "approved",
    "amount": "250.00",
    "currency": "GBP",
    "customer_email": "customer@example.com",
    "customer_name": "Alex Doe",
    "merchant_reference": "player-90431/dep-2207",
    "created_at": "...", "updated_at": "..."
  }
}

GET/payments

List your payments, newest first. Paginate with the before cursor.

QueryDescription
limit1-200 (default 50)
beforeCursor: pass the last id from the previous page
{ "ok": true, "data": [ /* payments */ ], "paging": { "limit": 50, "next_before": 123 } }

Payouts Pay your players

Payouts let you pay your own players - withdrawals from your cashier to a player's bank account. This is the reverse of a deposit and is distinct from settlement: settlement is when we automatically pay you (the merchant) your accumulated deposit balance; a payout is when you send money to one of your players. One neutral API covers every supported country.

Test first. A pe_test_ key simulates a payout (no money moves) so you can build your integration end-to-end. Live payouts must be enabled on your account before they will process; until then a live request returns 503 { "ok": false, "error": "Payouts are not yet enabled for live mode." }. Ask us to enable live payouts when you are ready.

POST/payouts

Create a payout to a player's bank account. The amount is in major units; the currency is implied by the country (CLP and COP are zero-decimal - send integer amounts).

Request body (JSON)

FieldRequiredDescription
countryyesECUADOR | CHILE | PERU | MEXICO | COLOMBIA. Selects the payout rail + currency.
amountyesMajor units, e.g. "250.00" (or integer "5000" for CLP / COP).
currencynoOptional override; defaults to the country currency.
beneficiaryyesThe player's payout details - see Beneficiary by country for the required sub-fields.
merchant_referencenoYour own id for this payout (e.g. "player-90431/wd-5512"). Echoed on the payout webhook and on GET /payouts. Max 128 chars.
idempotency_keynoA unique string per payout attempt. Retrying the same key returns the original payout instead of sending a duplicate. Also accepted as the Idempotency-Key header.

Beneficiary object

FieldDescription
nameBeneficiary full name (first + last). For a company beneficiary, the company name.
document_typeCase-sensitive ID type, per country (see each country block).
document_numberID document number.
account_numberDestination account number (the CLABE for Mexico).
bank_codeCase-sensitive bank code, per country (see each country block).
account_typeAccount type code, per country (e.g. savings / checking / CLABE).
cciPeru only - the interbank (CCI) number, different from account_number. For other countries this is optional and defaults to account_number.

Response 201

{
  "ok": true,
  "payout": {
    "reference": "PE-PO-XXXX-XXXX",
    "status": "pending",
    "amount": "250.00",
    "currency": "PEN",
    "country": "PERU",
    "merchant_reference": "player-90431/wd-5512"
  }
}

A payout starts pending. The final outcome arrives later as a payout webhook (and is reflected on GET /payouts). A 422 with a missing array is returned when required beneficiary fields are absent for the country.

Beneficiary requirements by country

Required beneficiary sub-fields differ per country. The blocks below are generated from the same rail data the payout API validates against, so they never drift.

Payout statuses

StatusMeaning
pendingAccepted and awaiting settlement to the player's account (initial state).
paidSettled - the funds reached the player's account.
bouncedRejected by the bank (e.g. invalid account, closed account, wrong code). Final.
revertedWas paid, then reversed - the funds were returned. Final.

Payout webhook events

When a payout reaches a terminal state we POST a webhook to your configured URL, signed with the same X-PE-Signature scheme as payment webhooks (see Webhooks for verification). Subscribe to these events in the dashboard.

EventWhen
payout.paidThe payout settled - the player received the funds.
payout.bouncedThe payout was rejected and did not pay out.
payout.revertedA previously-paid payout was reversed; funds returned.
{
  "id": "evt_...",
  "event": "payout.paid",
  "livemode": true,
  "created_at": "2026-06-18T12:00:00Z",
  "data": {
    "reference": "PE-PO-XXXX-XXXX",
    "merchant_reference": "player-90431/wd-5512",
    "status": "paid",
    "amount": "250.00",
    "currency": "PEN"
  }
}

Be idempotent on data.reference - a delivery can arrive more than once. livemode is false for sandbox payouts.

GET/payouts

List your payouts, newest first. A test key lists only sandbox payouts; a live key lists only live payouts. Paginate with the before cursor.

{ "ok": true, "data": [ /* payouts */ ], "paging": { "limit": 50, "next_before": 123 } }

Errors

Errors return a non-2xx status with { "ok": false, "error": "message" }. 401 means a missing/invalid/revoked API key.