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{ "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.
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.02
Create a payment
POST to/v1/paymentswith an amount in centimes. The answer contains acheckout_url.03
Send the customer to the checkout
Redirect them tocheckout_url. When they have paid you receive apayment.succeededwebhook, and the payment readspaidwhen you retrieve it.
Sandbox and live
Every store has both. A key only ever sees the payments of its own environment.
| Property | Sandbox | Live |
|---|---|---|
| Key | rais_sk_test_… | rais_sk_live_… |
| Payments | Test payments, with livemode set to false | Real payments, with livemode set to true |
| Money | Never moves. Nothing is credited or debited. | Credited to your Raïs account when SATIM confirms the payment |
| Checkout page | Shows a Test mode banner | No test banner |
| Availability | Always | After 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.
| 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.