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 version

Authentication

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.

POST/v1/payment-links

amount_minorinteger, required100 – 100 000 000 (1.00 – 1 000 000.00 MAD)
currencystringMAD (default)
descriptionstring, requiredShown to the customer, max 500
referencestringYour order/invoice id, unique per merchant
customer_name / customer_emailstringOptional prefill
usagesingle | multiplesingle (default) completes after one payment
expires_atISO-8601Optional, must be in the future
metadataobjectUp 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…"
  }
}
400validation_failed, invalid_json
401authentication_required, invalid_api_key
403forbidden, merchant_not_active
404not_found
409idempotency_key_reused, link_not_payable, refund_not_allowed, amount_exceeds_refundable
429rate_limited
5xxinternal_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.