Paymint API
A predictable REST API to create payment links, follow transactions and issue refunds. All amounts are integers in minor units (centimes): 25000 means 250.00 MAD.
Base URL https://api.paymint.ma/v1
Format JSON over HTTPS · UTF-8
Versioning /v1 — breaking changes only ship in a new versionAuthentication
Authenticate with a secret key from Dashboard → Developers. Keys start with pm_test_ or pm_live_. Send them only from your server — never in a browser or mobile app.
curl https://api.paymint.ma/v1/transactions \
-H "Authorization: Bearer pm_test_xxxxxxxxxxxxxxxxxxxxxxxx"Keys are stored hashed; a lost key cannot be recovered — revoke it and create a new one. Requests are limited to 100 per minute per key.
Payment links
POST/v1/payment-links
| amount_minor | integer, required | 100 – 100 000 000 (1.00 – 1 000 000.00 MAD) |
| currency | string | MAD (default) |
| description | string, required | Shown to the customer, max 500 |
| reference | string | Your order/invoice id, unique per merchant |
| customer_name / customer_email | string | Optional prefill |
| usage | single | multiple | single (default) completes after one payment |
| expires_at | ISO-8601 | Optional, must be in the future |
| metadata | object | Up to 20 string key/values |
curl -X POST https://api.paymint.ma/v1/payment-links \
-H "Authorization: Bearer pm_test_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-1042" \
-d '{"amount_minor": 25000, "description": "Website development", "reference": "INV-1042"}'{
"id": "5d6f2c1e-…",
"object": "payment_link",
"code": "8F3K92AB",
"url": "https://paymint.ma/p/8F3K92AB",
"amount_minor": 25000,
"currency": "MAD",
"description": "Website development",
"reference": "INV-1042",
"usage": "single",
"status": "active",
"paid_count": 0,
"created_at": "2026-09-27T10:24:00Z"
}GET/v1/payment-links
GET/v1/payment-links/:id
POST/v1/payment-links/:id/disable
Link status: active, disabled, expired, completed.
Checkout
Send your customer to the link url (or show it as a QR code). Paymint hosts a mobile-first checkout in French, Arabic and English; the card is entered on the licensed provider's secure page. Paymint never receives card numbers or CVV.
Transactions
GET/v1/transactions?status=paid&limit=20
GET/v1/transactions/:id
Lifecycle: created → pending → processing → paid, or failed · cancelled · expired. After payment: partially_refunded · refunded. States only move forward and are changed exclusively by Paymint after verifying the provider's confirmation.
{
"id": "0b8f…", "object": "transaction", "status": "paid",
"amount_minor": 25000, "fee_minor": 725, "net_minor": 24275, "refunded_minor": 0,
"currency": "MAD", "reference": "PM260927X8K2M4QZ7H1C",
"customer": { "name": "Amina", "email": "amina@example.ma" },
"payment_link_id": "5d6f…", "provider": "cmi", "livemode": true,
"paid_at": "2026-09-27T10:26:12Z"
}Refunds
POST/v1/refunds
curl -X POST https://api.paymint.ma/v1/refunds \
-H "Authorization: Bearer pm_live_..." \
-H "Idempotency-Key: refund-1042" \
-d '{"transaction_id": "0b8f…", "amount_minor": 10000, "reason": "Partial refund"}'Omit amount_minor to refund the remaining balance. Refund status: requested, processing, succeeded, failed. Over-refunding is impossible: concurrent requests are serialised and reserved.
GET/v1/refunds/:id
Payment confirmations
Paymint confirms a payment only from the provider's signed server-to-server notification — never from the customer's browser. Each notification is verified, de-duplicated and applied exactly once, and the amount and currency are checked against what was requested. To follow payments from your backend, poll GET /v1/transactions/:id or watch the dashboard; outgoing merchant webhooks are on the roadmap.
Errors
HTTP/1.1 409 Conflict
{
"error": {
"type": "invalid_request_error",
"code": "amount_exceeds_refundable",
"message": "Refund exceeds the refundable amount",
"param": "amount_minor",
"request_id": "req_4f1c…"
}
}| 400 | validation_failed, invalid_json |
| 401 | authentication_required, invalid_api_key |
| 403 | forbidden, merchant_not_active |
| 404 | not_found |
| 409 | idempotency_key_reused, link_not_payable, refund_not_allowed, amount_exceeds_refundable |
| 429 | rate_limited |
| 5xx | internal_error, provider_unavailable — safe to retry with the same Idempotency-Key |
Idempotency & pagination
Send an Idempotency-Key header (8–255 chars) on POST requests. Retrying with the same key and body returns the original response with Idempotent-Replayed: true; the same key with a different body returns 409. Keys are kept for 7 days.
Lists return { "object": "list", "data": [...], "has_more": true }. Pass limit (1–100) and starting_after (the last id you received) to page.