Skip to main content

The Raïs API

Create payments, read their status and receive signed webhooks. This page is the overview; the documentation has the full reference.

Base URL
https://api.rais.app/v1
Authorization
Bearer <secret key>
Amounts
Integers in centimes, DZD only
Rais-Version
2026-10-26
curl https://api.rais.app/v1/payments \  -H "Authorization: Bearer $RAIS_SECRET_KEY" \  -H "Idempotency-Key: order-1042" \  -H "Content-Type: application/json" \  -d '{    "amount": 1250000,    "currency": "DZD",    "description": "Order #1042",    "reference": "order-1042",    "success_url": "https://shop.example.dz/thanks",    "cancel_url": "https://shop.example.dz/cart"  }'
# The answer contains a checkout_url: send the customer there
Response201 Created
{  "id": "5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33",  "object": "payment",  "status": "pending",  "amount": 1250000,  "currency": "DZD",  "reference": "order-1042",  "livemode": false,  "checkout_url": "https://pay.rais.app/pay/5b1c2f4e-9d3a-4c61-8a7e-0f2d6b9e1a33",  "expires_at": 1793086400}

Create your first payment

Everything here works in the sandbox with a test key. No real money moves.

  1. 01

    Get a sandbox key

    Create a business account. Every store can create sandbox keys from the start. A secret key is shown once, so copy it to your server's environment. Never put it in a browser or a mobile app.
  2. 02

    Create a payment

    POST to /v1/payments with an amount in centimes. The answer contains a checkout_url.
  3. 03

    Send the customer to the checkout

    Redirect them to checkout_url. When they have paid you receive a payment.succeeded webhook, and the payment reads paid when you retrieve it.

Sandbox and live

Every store has both. A key only ever sees the payments of its own environment.

Sandbox compared with live
PropertySandboxLive
Keyrais_sk_test_…rais_sk_live_…
PaymentsTest payments, with livemode set to falseReal payments, with livemode set to true
MoneyNever moves. Nothing is credited or debited.Credited to your Raïs account when SATIM confirms the payment
Checkout pageShows a Test mode bannerNo test banner
AvailabilityAlwaysAfter your business is verified

What the API offers

Eleven operations. Lists are newest first and paged with a cursor. Errors always have the same shape.

Operations of the Raïs API
Operation
POST/v1/paymentsCreate a payment and get its checkout_url
GET/v1/paymentsList payments, newest first
GET/v1/payments/{id}Retrieve a payment
POST/v1/payments/{id}/cancelCancel a pending payment
POST/v1/payments/{id}/refundsRefund a payment, in full or in part
GET/v1/payments/{id}/receiptGet a signed link to the PDF receipt of a paid payment
GET/v1/refundsList refunds
GET/v1/refunds/{id}Retrieve a refund
GET/v1/eventsList events from the last 30 days
GET/v1/events/{id}Retrieve an event
GET/v1/accountRetrieve your store and its environment
  • Idempotency

    Send an Idempotency-Key when you create a payment or a refund. Repeat the request with the same key and you get the original answer back, for 24 hours.

  • Errors

    Every error is JSON with a type, a stable code, a message and a request_id that is also in the Rais-Request-Id header.

  • Scopes and limits

    Keys carry scopes (payments:read, payments:write, refunds:write, events:read). Each key may make 60 requests per second, with bursts of 120.

  • Server side only

    The API sends no CORS headers on purpose. Call it from your server and give your front end the checkout_url.

Webhooks

Raïs calls your server when a payment or a refund changes. Every delivery is signed, so you can check where it came from.

  • payment.succeededThe payment was paid.
  • payment.failedThe bank declined it, or an error occurred.
  • payment.cancelledThe payer cancelled.
  • payment.reversedThe payment was reversed.
  • refund.succeededA refund went through.
  • refund.failedA refund was refused.

Each request carries a Rais-Signature header: t=<timestamp>,v1=<signature>. The signature is the HMAC-SHA256 of the timestamp, a dot and the raw body, made with your endpoint's signing secret. Reject timestamps older than five minutes. Answer with a 2xx status; otherwise Raïs tries up to 8 times over about 21 hours.

import { createHmac, timingSafeEqual } from "node:crypto";
// rawBody: the request body exactly as received. header: the Rais-Signature header.export function verifyRaisSignature(rawBody, header, secret, toleranceSeconds = 300) { const items = header.split(",").map((part) => part.split("=")); const t = items.find(([key]) => key === "t")?.[1]; const signatures = items.filter(([key]) => key === "v1").map(([, value]) => value); if (!t || Math.abs(Date.now() / 1000 - Number(t)) > toleranceSeconds) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest(); return signatures.some((hex) => { const given = Buffer.from(hex, "hex"); return given.length === expected.length && timingSafeEqual(given, expected); });}

Start in the sandbox

Create an account, copy a test key and make your first payment. The documentation covers the rest.