Skip to main content

Overview

Cartevo webhooks notify your system in near real‑time about account events (card creation, funding, withdrawals, errors, etc.).
  • Method: POST
  • Format: JSON
  • Headers: Content-Type: application/json
  • Expected response: any HTTP 2xx is treated as success (response body is ignored)
Your webhook URL is configured on your company account. For every event, Cartevo sends an HTTP request with the following shape:
{
  "event": "<event_name>",
  "data": {
    /* event-specific payload */
  }
}

Webhook Headers

Content-Type: application/json
User-Agent: Wavlet-Webhook/1.0
X-Webhook-Signature: sha256=<signature> (when configured)
X-Webhook-Id: <unique-webhook-id>
X-Webhook-Timestamp: <unix-timestamp>

Retry policy

If delivery fails (non‑2xx response or network error), Cartevo automatically retries up to 3 attempts, with 5 seconds between attempts.
AttemptDelayTotal Time
10s0s
25s5s
35s10s
45s15s
  • Max retries: 3
  • Timeout: 10 seconds per attempt
  • Logging: every attempt is logged; after 3 failures, delivery is marked permanently failed.
🚨 Tip: treat webhooks as at‑least‑once deliveries. Implement idempotency/deduplication on your receiver using stable business identifiers (e.g., transaction_id, card.id, wallet_id).

Setting Up Webhooks

Configure Your Webhook URL

Set your webhook URL in your company settings:

Supported Events

Payment Events

payment.collect

Emitted when a payment collection (pay‑in) is initiated.
{
  "event": "payment.collect",
  "data": {
    "transaction_id": "550e8400-e29b-41d4-a716-446655440000",
    "amount": 1000,
    "currency": "XAF",
    "operator": "mtn",
    "phone_number": "237XXXXXXXXX",
    "initiated_at": "2025-01-08T12:00:00.000Z"
  }
}

Card Events

card.created

Emitted when a new card is successfully created for a customer. Payload:
{
  "event": "card.created",
  "data": {
    "card": {
      "id": "card_abc123xyz789",
      "customer_id": "cust_def456uvw012",
      "brand": "VISA | MASTERCARD",
      "masked_number": "4111********1111",
      "balance": 100.0,
      "currency": "USD"
    },
    "card_issuarance": {
      "status": "SUCCESS",
      " transaction_id": "trx_id",
      "amount": "e.g 2",
      "description": "description"
    },
    "card_fund": {
      "status": "SUCCESS",
      "transaction_id": "trx_id",
      "amount": "e.g 100",
      "description": "description"
    }
  }
}
Fields:
FieldTypeDescription
idstringUnique card identifier
customer_idstringCustomer who owns the card
brandstringCard brand (VISA, MASTERCARD)
masked_numberstringMasked card number for display
balancenumberInitial card balance
currencystringCard currency (always USD)

card.fund

Emitted when a card is funded with money from a wallet. Payload:
{
  "event": "card.fund",
  "data": {
    "transaction_id": "txn_ghi789klm345",
    "card_id": "card_abc123xyz789",
    "amount": 50.0,
    "currency": "USD",
    "fee_amount": 0.0
  }
}
Fields:
FieldTypeDescription
transaction_idstringTransaction reference
card_idstringCard being funded
amountnumberAmount added to card
currencystringTransaction currency
fee_amountnumberFee charged for funding (usually 0)

card.withdraw

Emitted when money is withdrawn from a card back to the wallet. Payload:
{
  "event": "card.withdraw",
  "data": {
    "transaction_id": "txn_nop678qrs234",
    "card_id": "card_abc123xyz789",
    "amount": 30.0,
    "currency": "USD",
    "fee_amount": 0.0
  }
}
Fields:
FieldTypeDescription
transaction_idstringTransaction reference
card_idstringCard being withdrawn from
amountnumberAmount withdrawn
currencystringTransaction currency
fee_amountnumberFee charged for withdrawal (usually 0)

card.withdraw.failed

Emitted when a card withdrawal attempt fails. Payload:
{
  "event": "card.withdraw.failed",
  "data": {
    "transaction_id": "txn_tuv901wxy567",
    "card_id": "card_abc123xyz789",
    "amount": 200.0,
    "error": "Insufficient balance or provider error"
  }
}
Fields:
FieldTypeDescription
transaction_idstringFailed transaction reference
card_idstringCard attempted to withdraw from
amountnumberAmount that failed to withdraw
errorstringError message describing failure reason

card.terminated

Emitted when a card is permanently terminated and any remaining balance is refunded to the company’s USD wallet. Payload:
{
  "event": "card.terminated",
  "data": {
    "cardId": "550e8400-e29b-41d4-a716-446655440000",
    "cardNumber": "****1234",
    "status": "TERMINATED",
    "terminatedAt": "2025-01-08T12:00:00.000Z",
    "refundAmount": 50.0,
    "refundProcessed": true,
    "reason": "Card terminated"
  }
}
Fields:
FieldTypeDescription
cardIdstringUnique identifier of the terminated card (UUID)
cardNumberstringMasked card number showing only the last 4 digits (e.g., ****1234)
statusstringAlways "TERMINATED" for this event
terminatedAtstringISO 8601 timestamp when the card was terminated
refundAmountnumberAmount refunded to the company’s USD wallet (0 if card had no balance)
refundProcessedbooleanIndicates whether the refund was successfully processed (true or false)
reasonstringReason for termination (typically "Card terminated")
Important Notes:
  • When a card is terminated, any remaining balance is automatically refunded to the company’s USD wallet
  • The card balance is set to zero and the card becomes unusable
  • This action is irreversible - once terminated, a card cannot be reactivated
  • Webhook failures do not prevent card termination from completing

Transaction Events

transaction.settlement.completed

Emitted when a transaction is settled/cleared.
{
  "event": "transaction.settlement.completed",
  "data": {
    "transactionId": "txn_abc123",
    "cardId": "card_xyz789",
    "amount": 25.0,
    "currency": "USD",
    "type": "SETTLEMENT",
    "status": "SUCCESS",
    "createdAt": "2025-01-06T13:00:00Z",
    "merchant": {
      "name": "Merchant Ltd",
      "country": "US",
      "city": "Seattle"
    }
  }
}

transaction.authorization.created

Emitted when a card payment authorization succeeds. Payload:
{
  "event": "transaction.authorization.created",
  "data": {
    "transactionId": "txn_abc123",
    "cardId": "card_xyz789",
    "amount": 25.0,
    "currency": "USD",
    "type": "AUTHORIZATION",
    "status": "SUCCESS",
    "createdAt": "2025-01-06T13:00:00Z",
    "merchant": {
      "name": "Amazon.com",
      "country": "US",
      "city": "Seattle"
    }
  }
}
Fields:
FieldTypeDescription
transactionIdstringTransaction identifier
cardIdstringCard used for transaction
amountnumberTransaction amount
currencystringTransaction currency
typestringTransaction type (AUTHORIZATION)
statusstringTransaction status (SUCCESS)
createdAtstringISO 8601 timestamp
merchantobjectMerchant information (name, country, city)
mccstringMerchant Category Code (optional)
midstringMerchant ID (optional)
approvalCodestringAuthorization approval code (optional)
cardBalanceBeforenumberCard balance before (optional)
cardBalanceAfternumberCard balance after (optional)

transaction.authorization.declined

Emitted when a payment authorization is declined. Payload:
{
  "event": "transaction.authorization.declined",
  "data": {
    "transactionId": "txn_def456",
    "cardId": "card_xyz789",
    "customerId": "cust_def456",
    "amount": 150.0,
    "currency": "USD",
    "type": "AUTHORIZATION",
    "status": "FAILED",
    "createdAt": "2025-01-06T13:00:00Z",
    "merchant": {
      "name": "Expensive Store",
      "country": "US"
    },
    "declineReason": "Insufficient funds"
  }
}
Fields:
FieldTypeDescription
transactionIdstringTransaction identifier
cardIdstringCard used for transaction
customerIdstringCustomer identifier
amountnumberTransaction amount
currencystringTransaction currency
typestringTransaction type (AUTHORIZATION)
statusstringTransaction status (FAILED)
createdAtstringISO 8601 timestamp
merchantobjectMerchant information
declineReasonstringReason for decline

transaction.reversal.completed

Emitted when a transaction is reversed (usually within 24 hours). Payload:
{
  "event": "transaction.reversal.completed",
  "data": {
    "transactionId": "txn_pqr678",
    "cardId": "card_xyz789",
    "amount": 25.0,
    "currency": "USD",
    "type": "REVERSAL",
    "status": "SUCCESS",
    "merchant": {
      "name": "Amazon.com",
      "country": "US"
    },
    "reversalReason": "Customer cancelled order",
    "createdAt": "2025-01-06T13:00:00Z"
  }
}
Fields:
FieldTypeDescription
transactionIdstringTransaction identifier
cardIdstringCard used for transaction
amountnumberReversed amount
currencystringTransaction currency
typestringTransaction type (REVERSAL)
statusstringTransaction status (SUCCESS)
merchantobjectMerchant information
reversalReasonstringReason for reversal
createdAtstringISO 8601 timestamp

transaction.refund.completed

Emitted when a refund is processed (usually after settlement). Payload:
{
  "event": "transaction.refund.completed",
  "data": {
    "transactionId": "txn_stu901",
    "cardId": "card_xyz789",
    "amount": 25.0,
    "currency": "USD",
    "type": "REFUND",
    "status": "SUCCESS",
    "merchant": {
      "name": "Amazon.com",
      "country": "US"
    },
    "refundReason": "Product returned",
    "createdAt": "2025-01-06T13:00:00Z"
  }
}
Fields:
FieldTypeDescription
transactionIdstringTransaction identifier
cardIdstringCard used for transaction
amountnumberRefunded amount
currencystringTransaction currency
typestringTransaction type (REFUND)
statusstringTransaction status (SUCCESS)
merchantobjectMerchant information
refundReasonstringReason for refund
createdAtstringISO 8601 timestamp

transaction.funding.completed

Emitted when a card funding transaction completes.
{
  "event": "transaction.funding.completed",
  "data": {
    "transactionId": "txn_fund001",
    "cardId": "card_xyz789",
    "amount": 50.0,
    "currency": "USD",
    "type": "FUND",
    "status": "SUCCESS",
    "createdAt": "2025-01-06T12:00:00Z"
  }
}

transaction.withdrawal.completed

Emitted when a withdrawal from card completes.
{
  "event": "transaction.withdrawal.completed",
  "data": {
    "transactionId": "txn_wd001",
    "cardId": "card_xyz789",
    "amount": 30.0,
    "currency": "USD",
    "type": "WITHDRAW",
    "status": "SUCCESS",
    "createdAt": "2025-01-06T12:30:00Z"
  }
}

transaction.crossborder.charged

Emitted when a cross‑border charge related to a purchase is recorded.
{
  "event": "transaction.crossborder.charged",
  "data": {
    "transactionId": "txn_cb001",
    "cardId": "card_xyz789",
    "amount": 25.0,
    "currency": "USD",
    "type": "CROSS_BORDER_CHARGE",
    "status": "SUCCESS",
    "createdAt": "2025-01-06T13:00:00Z",
    "feeAmount": 1.13
  }
}

transaction.terminated

Emitted when a termination‑type transaction is recorded for a card.
{
  "event": "transaction.terminated",
  "data": {
    "transactionId": "txn_term001",
    "cardId": "card_xyz789",
    "amount": 0,
    "currency": "USD",
    "type": "TERMINATION",
    "status": "SUCCESS",
    "createdAt": "2025-01-06T15:00:00Z"
  }
}

Fee Events

fee.payment_failure.charged

Emitted when a fee is charged for a failed payment transaction. Payload:
{
  "event": "fee.payment_failure.charged",
  "data": {
    "type": "payment_failure",
    "amount": 0.5,
    "currency": "USD",
    "transactionId": "txn_ghi789",
    "description": "Payment failure fee - Expensive Store - 150.00 USD - 01/06/2025, 02:00 PM",
    "createdAt": "2025-01-06T14:00:05Z"
  }
}

fee.crossborder.charged

Emitted when a cross-border transaction fee is charged. Payload:
{
  "event": "fee.crossborder.charged",
  "data": {
    "type": "crossborder",
    "amount": 1.13,
    "currency": "USD",
    "transactionId": "txn_jkl012",
    "cardId": "card_xyz789",
    "description": "Cross-border fee - Amazon.com - 25.00 USD - 01/06/2025, 01:00 PM",
    "createdAt": "2025-01-06T13:00:20Z"
  }
}

Debts Events

debt.recovery.pending

Recently Added: This webhook notifies companies in real-time when a payment fee debt has been created due to the incapacity to collect fee from the card. Triggered In 2 Scenarios:
  • Payment Failure Fee - When a card payment fails and the company wallet cannot cover the failure fee
  • Cross-Border Fee - When a cross-border transaction occurs and the company wallet cannot cover the fee
Payload:
{
  "event": "fee.payment_failure.charged",
  "data": {
  "amount": "number",              // Fee amount in USD
  "currency": "USD",
  "type": "payment_failure_fee" / "crossborder_fee",
  "cardId": "56dd86c7-255f-4f76-bbbb-fdb33c55fb6c",
  "transactionId": "4e3f4fc1-08d9-49a6-b1af-b6495f0a5c2c"
}

}

Purpose:

  • Immediately notify companies when debts are created
  • Maintain transparency in the debt recovery proce
Fields: Same as other fee events, but type is crossborder. Cross-Border Fee Calculation:
Fee = (Transaction Amount × 2.5%) + $0.50

Example:
Transaction: $25.00
Fee: ($25.00 × 2.5%) + $0.50 = $0.625 + $0.50 = $1.13
Fee Collection Priority:
  1. Card Balance – Fees are first deducted from the available balance on the card.
  2. Debt Creation – If the card balance is insufficient, the system automatically records the remaining amount as a debt to be settled.

Customer Events

customer.created

Emitted when a new customer is created in the system. Payload:
{
  "event": "customer.created",
  "data": {
    "id": "cust_abc123",
    "first_name": "John",
    "last_name": "Doe",
    "email": "[email protected]",
    "phone_number": "+1234567890",
    "country": "cust_country",
    "createdAt": "2025-01-07T11:00:00Z"
  }
}
Fields:
FieldTypeDescription
idstringUnique customer identifier
firstNamestringCustomer’s first name
lastNamestringCustomer’s last name
emailstringCustomer’s email address
phone_numberstringCustomer’s phone number
countrystringCustomer’s country
createdAtstringISO 8601 timestamp

Best Practices

Response Handling

Always return 200 OK within 10 seconds, before processing:
// GOOD
app.post("/webhooks", async (req, res) => {
  // Acknowledge immediately
  res.status(200).json({ received: true });

  // Process async
  processWebhook(req.body.event, req.body.data).catch((error) =>
    console.error("Webhook error:", error)
  );
});

// BAD
app.post("/webhooks", async (req, res) => {
  // Processing delays response
  await processWebhook(req.body.event, req.body.data);
  res.status(200).json({ received: true });
});

Idempotency

Webhooks may be delivered more than once. Always check for duplicates using transaction_id, card.id, or wallet_id:
const processedWebhooks = new Set<string>();

async function processWebhook(event: string, data: any) {
  const webhookId = data.transactionId || data.cardId || data.card?.id;

  if (processedWebhooks.has(webhookId)) {
    console.log("Duplicate webhook ignored:", webhookId);
    return;
  }

  processedWebhooks.add(webhookId);
  await handleEvent(event, data);
}

Security

  • Use HTTPS only - HTTP endpoints are not supported
  • Verify webhook signatures (when configured) using the X-Webhook-Signature header
  • Validate request format - Ensure payload matches expected structure
  • Implement rate limiting - Protect your endpoint from abuse
  • IP whitelisting - Whitelist Cartevo IP addresses (when available)

Error Handling

Handle errors gracefully without exposing internal details:
app.post("/webhooks", async (req, res) => {
  // Always respond with 200 first
  res.status(200).json({ received: true });

  try {
    await processWebhook(req.body.event, req.body.data);
  } catch (error) {
    // Log error internally only
    console.error("Webhook processing error:", {
      event: req.body.event,
      error: error.message,
    });
    // Alert monitoring system
  }
});

Logging

Record important fields for supportability:
app.post("/webhooks", (req, res) => {
  const { event, data } = req.body;

  // Log webhook
  console.log({
    timestamp: new Date().toISOString(),
    event,
    webhookId: req.headers["x-webhook-id"],
    transactionId: data.transactionId,
    cardId: data.cardId || data.card?.id,
    walletId: data.walletId,
  });

  res.status(200).json({ received: true });
  processWebhook(event, data);
});

Sample Receiver Response

HTTP/1.1 200 OK
Content-Type: application/json

{"received": true}

Troubleshooting

IssueSolution
Receiving nothingVerify the configured webhook URL and public reachability
4xx errorsYour receiver rejected the request (auth/validation). Adjust your endpoint to accept the format above
5xx errorsYour receiver errored; Cartevo will retry up to 3 times
Timeout errorsRespond within 10 seconds. Process webhooks asynchronously
Duplicate webhooksImplement idempotency checks using transaction IDs

Complete Event Reference

Card Events (5 events)

  • card.created - Card successfully created
  • card.fund - Card funded with money
  • card.withdraw - Money withdrawn from card
  • card.withdraw.failed - Withdrawal failed
  • card.terminated - Card permanently terminated

Transaction Events (4 events)

  • transaction.authorization.created - Payment authorized
  • transaction.authorization.declined - Payment declined
  • transaction.reversal.completed - Transaction reversed
  • transaction.refund.completed - Refund processed

Fee Events (2 events)

  • fee.payment_failure.charged - Failure payment fee charged ($0.50)
  • fee.crossborder.charged - Cross-border fee charged (2.5% + $0.50)

Customer Events (1 event)

  • customer.created - Customer created
Total: 18 webhook events