Skip to main content
POST
/
payment
/
collect
Initiate a payment
curl --request POST \
  --url https://api.cartevo.co/api/v1/payment/collect \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "operator": "mtn",
  "country": "CM",
  "phone_number": "237670000000",
  "amount": 1000,
  "currency": "XAF",
  "notify_url": "https://example.com/webhook/payment",
  "external_id": "<string>",
  "reference_id": "<string>",
  "lang": "<string>",
  "purpose": "<string>"
}
'
{
  "success": true,
  "message": "Collection initiated successfully",
  "data": {
    "transaction_id": "550e8400-e29b-41d4-a716-446655440000",
    "external_id": "COLL-1234567890-abc12345",
    "status": "PENDING",
    "amount": 1000,
    "currency": "XAF",
    "operator": "mtn",
    "country": "CM",
    "phone_number": "237670000000",
    "initiated_at": "2025-01-08T12:00:00.000Z"
  }
}

Overview

POST /payment/collect initiates a pay-in: it charges a mobile-money account and credits the proceeds to your company’s pay-in wallet for the matching country/currency. The call returns immediately with status: PENDING; the final outcome (SUCCESS or FAILED) reaches you asynchronously via webhook (or via notify_url if you supply one). For some operators, the end user must approve the charge with an OTP or USSD step — see the country/operator matrix in Payment guidelines.

When to use it

  • You need to charge an end user’s mobile-money account to settle a bill, top up a balance, or fund a service.
For the reverse direction (sending money out to a mobile-money account), use POST /payment/payout.

Prerequisites

  • Your company has a wallet for the requested (country, currency) pair. If not, create it first via POST /wallets.
  • The combination of country, currency, and operator is supported — see Payment guidelines.

Request

Headers

NameRequiredDescription
AuthorizationYesBearer <access_token>
Content-TypeYesapplication/json

Body

FieldTypeRequiredConstraints / format
operatorstringYesOne of mtn, orange, moov, airtel, mpesa, afrimoney, vodacom, wave, wligdicash, expresso, free, tmoney, celtiis, coris. Lowercase.
countrystringYesISO 3166-1 alpha-2, uppercase (e.g. CM, CI, SN). Selects the wallet.
phone_numberstringYesCountry code + local number, no + (e.g. 237670000000). This format is specific to payments; other endpoints use E.164 with +.
amountnumberYesMinimum 1. Unit is the major currency unit of currency.
currencystringYesISO 4217. Must match a wallet you own. Common values: XAF, XOF, CDF, GNF, USD.
external_idstringNoIdempotency key. Up to 255 chars. If you supply the same value twice, the second call is rejected with 400. Auto-generated if omitted (you lose idempotency).
notify_urlstringNoHTTPS URL to receive the final status webhook. If omitted, only the company-level webhook is fired.
reference_idstringNoFree-form reference shown in the dashboard. Up to 255 chars.
langstringNoTwo-letter language code for any operator-side prompts (e.g. en, fr).
purposestringNoFree-form description of why the collection is happening (e.g. "Invoice payment").
{
  "operator": "mtn",
  "country": "CM",
  "phone_number": "237670000000",
  "amount": 1000,
  "currency": "XAF",
  "external_id": "ORD-2026-001",
  "notify_url": "https://your-app.com/webhooks/cartevo",
  "purpose": "Invoice payment for May 2026"
}

Idempotency

Always supply your own external_id. Cartevo rejects duplicates with 400 (after the first call), so storing the value before you send the request and reusing it on retry is safe. If you let Cartevo auto-generate one, you lose this protection.

Response

200 — Collection initiated

{
  "success": true,
  "statusCode": 200,
  "message": "Collection initiated successfully",
  "data": {
    "transaction_id": "550e8400-e29b-41d4-a716-446655440000",
    "external_id": "ORD-2026-001",
    "status": "PENDING",
    "amount": 1000,
    "currency": "XAF",
    "operator": "mtn",
    "country": "CM",
    "phone_number": "237670000000",
    "initiated_at": "2026-05-09T10:15:00.000Z"
  }
}
FieldTypeDescription
transaction_idstringCartevo transaction ID (UUID). Use this for GET /payment/transactions/{id}/status.
external_idstringEchoes your idempotency key (or the auto-generated one).
statusstringPENDING immediately after this call. Other values: PROCESSING, SUCCESS, FAILED.
initiated_atstringISO 8601 (UTC).

Lifecycle

PENDING → PROCESSING → SUCCESS
                    ↘ FAILED
  • PENDING — Cartevo accepted the request; the operator has been notified.
  • PROCESSING — The operator is awaiting user action (OTP / USSD) or processing internally.
  • SUCCESS — Funds have been credited to your pay-in wallet. The payment.collect webhook fires.
  • FAILED — End user rejected, insufficient funds at the operator, OTP timeout, etc. See error_message in the status endpoint.
PROCESSING can last up to 15 minutes for OTP-required operators. Beyond that, treat as failed and reconcile via the status endpoint.

Error responses

Statusmessage exampleTrigger
400"Validation failed: country must be a valid ISO 3166-1 alpha-2 code"Field-level validation failed.
400"Duplicate external_id"An earlier call used the same external_id.
400"No wallet for this country and currency"You don’t own a wallet for the (country, currency) pair.
400"Operator not available for this country/currency"See Payment guidelines.
401"Unauthorized"Missing or expired token.

Webhooks fired

  • payment.collect — fires when the collection is initiated, then again on status changes (SUCCESS / FAILED).
  • If you supplied notify_url, it receives a copy of each status change.

Code examples

cURL
curl -X POST https://api.cartevo.co/api/v1/payment/collect \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "operator": "mtn",
    "country": "CM",
    "phone_number": "237670000000",
    "amount": 1000,
    "currency": "XAF",
    "external_id": "ORD-2026-001"
  }'
Node.js (axios)
const externalId = `ORD-${Date.now()}`;
await db.savePendingCollection(externalId);

const { data } = await axios.post(
  "https://api.cartevo.co/api/v1/payment/collect",
  {
    operator: "mtn",
    country: "CM",
    phone_number: "237670000000",
    amount: 1000,
    currency: "XAF",
    external_id: externalId,
    notify_url: "https://your-app.com/webhooks/cartevo",
  },
  { headers: { Authorization: `Bearer ${token}` } }
);

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Body

application/json
operator
enum<string>
required
Available options:
mtn,
orange,
moov,
airtel,
mpesa,
afrimoney,
vodacom,
wave,
wligdicash,
expresso,
free,
tmoney,
celtiis,
coris
Example:

"mtn"

country
string
required
Pattern: ^[A-Z]{2}$
Example:

"CM"

phone_number
string
required
Example:

"237670000000"

amount
number
required
Required range: x >= 1
Example:

1000

currency
string
required
Example:

"XAF"

notify_url
string
Example:

"https://example.com/webhook/payment"

external_id
string

Idempotency key (up to 255 chars). Duplicate values are rejected with 400 after the first call. Auto-generated if omitted.

reference_id
string

Free-form reference shown in the dashboard (up to 255 chars).

lang
string

Two-letter language code for operator-side prompts (e.g. en, fr).

purpose
string

Free-form description of the payment's purpose.

Response

Collection initiated successfully

success
boolean
Example:

true

message
string
Example:

"Collection initiated successfully"

data
object