Skip to main content

Fiat Payin

A fiat payin accepts a fiat payment from a customer (via mobile money or bank) and converts it into stablecoins, settling the equivalent amount into a Celar wallet. A rate quote is required before submission.

Examples use the sandbox environment. For production, replace api.sandbox.celar.io with api.celar.io.

How it works

  1. Fetch a rate from the Rates API: get the fiatAmount and a signature
  2. Submit a fiat_payin transfer with the customer's fiat payment details
  3. The customer sends the fiat amount via their mobile money provider
  4. Celar confirms receipt and converts it to stablecoins
  5. Stablecoins are settled to the destination Celar wallet
  6. A webhook fires when the status moves to settled
Rate validity

Rate quotes are valid for 5 minutes. Submit the transfer before the signature expires.

Prerequisites

  • A valid customer_id with payment details: see Register a Customer
  • A rate quote from the Rates API
  • A unique Idempotency-Key for the request

Step 1: Get a rate

Before submitting, fetch a rate quote from the Rates API. This returns the fiatAmount and signature you need for the next step.

Step 2: Submit the transfer

curl --request POST \
--url https://api.sandbox.celar.io/api/v1/payments/transfer \
--header 'Authorization: Bearer <your_api_key>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <unique_uuid>' \
--data '{
"operationType": "fiat_payin",
"customer_id": "cr_2ae76bc76f03437d",
"amount": 500,
"signature": "sig_8c23dc5c3140a72b",
"source": {
"currency": "KES",
"description": "Customer deposit via MPESA",
"reference": "REF-23923",
"serviceProvider": "MPESA",
"paymentMethod": "mobile",
"phoneNumber": "+254712345678"
},
"destination": {
"currency": "USDC",
"chain": "base",
"address": "0xabc123..."
}
}'

Response

{
"success": true,
"message": "Payin is pending. Please check webhook"
}

The fiat payin is asynchronous: monitor your webhook or poll the payment status for updates.

US customers

The signature field is not required. Instead, provide source.transfer_type to specify the funding method.

transfer_typeDescription
wireBank wire transfer. Returns wire instructions for the customer to follow
ach_debitACH pull from the customer's linked Plaid bank account (requires prior Plaid linkage)
curl --request POST \
--url https://api.sandbox.celar.io/api/v1/payments/transfer \
--header 'Authorization: Bearer <your_api_key>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <unique_uuid>' \
--data '{
"operationType": "fiat_payin",
"customer_id": "cr_2ae76bc76f03437d",
"amount": 500,
"source": {
"currency": "USD",
"description": "Wire deposit from US customer",
"paymentMethod": "bank",
"transfer_type": "wire"
},
"destination": {
"currency": "USDC",
"chain": "base"
}
}'

Wire response

{
"message": "Follow the details to complete payin",
"bankDetails": { }
}

bankDetails contains the wire instructions the customer uses to send funds.

ACH response

{
"message": "Payin completed successfully"
}

Fiat payin automations

US customers support a standing wire automation for fiat payins. When enabled, Celar automatically converts every incoming wire for that customer into stablecoins without requiring a per-transfer API call.

  • Set source.is_automation: true on a fiat payin request to activate it
  • source.transfer_type must be wire - automations are not supported for ach_debit
  • The response returns bankDetails with the fixed wire instructions the customer sends funds to
  • All subsequent wires from that customer are processed automatically

Field reference

FieldRequiredDescription
operationTypeYesfiat_payin
customer_idYesThe customer sending the fiat
amountYesFiat amount the customer is paying
signatureYes (non-US)Rate signature from the Rates API
source.currencyYesFiat currency code: e.g. KES
source.serviceProviderConditionalRequired for mobile payins: e.g. MPESA, MTN, AIRTEL
source.paymentMethodYesmobile for non-US customers
source.phoneNumberConditionalRequired for mobile payins (E.164 format)
source.accountNumberConditionalRequired for bank payins
source.transfer_typeConditionalRequired for US customers: wire or ach_debit
source.is_automationNoUS customers only. Set to true to create a standing wire automation for this customer
source.descriptionYesInternal description
source.referenceNoYour own reference ID
destination.currencyYesUSDC or USDT
destination.chainYesbase, polygon, arbitrum, or baseSepolia
destination.addressNoForward stablecoins to this address after conversion

See Payment Statuses and Webhooks for more.