# Create Account Source: https://docs.getcarbon.co/api-reference/accounts/create-account POST /v1/accounts Create a new virtual account for a customer/collection. ## Overview This endpoint creates a new virtual account based on the specified account type. * For third-party accounts (business use), set `third_party` to `true` and provide a `customer_id`. * For sub-accounts (collections), set `third_party` to `false` and provide an `account_name`. * If `third_party` is not specified, it defaults to `true`. ## Endpoint `POST /accounts` ## Request Body ### Standard Account Creation (Business Use) ```json theme={null} { "account_type": "static", "third_party": true, "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3" } ``` ### Sub-Account Creation (Collections) ```json theme={null} { "account_type": "static", "third_party": false, "account_name": "Collections" } ``` ## Response ```json theme={null} { "status": "success", "message": "account created successfully", "data": { "id": "5da78692-ffbc-4f1e-b60b-4febd75c4d66", "account": { "bank_name": "DEMO BANK", "bank_code": "565", "account_name": "OJO BENSON - DEMO BANK", "account_number": "0000000000", "client_id": "0000000" }, "owner": { "first_name": "ojo", "last_name": "erelu", "email": "erelu@yahoo.com", "phone": "08088000030" }, "account_type": "static", "mode": "sandbox", "created_at": "2024-03-07T15:03:03.000Z", "updated_at": "2024-03-07T15:03:03.000Z" } } ``` # Fetch Balance Source: https://docs.getcarbon.co/api-reference/accounts/fetch-balance GET /v1/accounts/{account_number}/balance Retrieve the balance of a specific account. ## Overview This endpoint retrieves the balance of a specific account identified by the account number. ### Request **Method:** `GET`\ **URL:** `/v1/accounts/{account_number}/balance` #### Parameters | Name | In | Type | Required | Description | | ---------------- | ------ | --------- | -------- | ------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `account_number` | Path | `integer` | Yes | The account number to retrieve. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Balance fetched successfully", "data": { "account_number": "0000000000", "balance": 0, "available_balance": 0, "business_account": true, "locked": false } } ``` # Flush Account Source: https://docs.getcarbon.co/api-reference/accounts/flush-account POST /v1/accounts/{account_number}/flush Flush the balance of an account to another account. ## Overview This endpoint flushes the balance of an account to another account. ### Request **Method:** `POST`\ **URL:** `/v1/accounts/{account_number}/flush` #### Parameters | Name | In | Type | Required | Description | | ---------------- | ------ | --------- | -------- | ---------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `account_number` | Path | `integer` | Yes | The account number to flush. | #### Request Body ```json theme={null} { "beneficiary": { "bank_code": "565", "bank_name": "CARBON", "account_number": "2499384072", "account_name": "CARBON BUSINESS DEMO - TAX" }, "reference": "12323E2445567786989090" } ``` ### Response **Status Code:** `201 Created`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Payout was initiated successfully", "data": { "amount": 10000, "total": 10000, "fee": 0, "reference": "12323E2445567786989090", "payment_reference": "206184559544833", "beneficiary": { "bank_code": "565", "bank_name": "CARBON", "account_number": "2499384072", "account_name": "CARBON BUSINESS DEMO - TAX" } } } ``` # Get Account Source: https://docs.getcarbon.co/api-reference/accounts/get-account GET /v1/accounts/{account_number} Retrieve details of a specific account by account number. ## Overview This endpoint retrieves details of a specific account using the account number. ## Endpoint `GET /accounts/{accountId}` ## Parameters * `accountId` (path): The unique identifier of the account. ## Response ```json theme={null} { "status": "success", "message": "Account fetched successfully", "data": { "id": "3529237a-8ae1-4d63-a6ad-419fae3b9f5e", "account": { "bank_name": "CARBON", "bank_code": "565", "account_name": "Ola Ajayi", "account_number": "6009490194", "balance": 350833340, "available_balance": 350833340, "locked": false }, "owner": { "id": "965c4bf7-e86b-45ca-b286-1ee6f32e991b", "first_name": "ola", "last_name": "ajayi", "email": "ola@yahoo.com", "phone": "08071000030" }, "is_static": true, "mode": "sandbox", "created_at": "2024-02-28T01:01:29.000Z", "updated_at": "2024-02-28T01:01:29.000Z" } } ``` # Fetch Accounts Source: https://docs.getcarbon.co/api-reference/accounts/get-accounts GET /v1/accounts Retrieve a list of accounts with pagination and optional filtering. ## Overview This endpoint retrieves a list of accounts with pagination. It fetches a specified number of accounts per page, as per the provided `page` and `limit` parameters. You can also filter results by `account_number` or `account_name`. ### Request **Method:** `GET`\ **URL:** `/v1/accounts` #### Parameters | Name | In | Type | Required | Description | | ---------------- | ------ | --------- | -------- | ------------------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `page` | Query | `integer` | No | Page number for pagination. | | `limit` | Query | `integer` | No | Number of accounts per page. | | `account_number` | Query | `string` | No | Filter accounts by a specific account number. | | `account_name` | Query | `string` | No | Filter accounts by account name (partial match). | | `customer_id` | Query | `string` | No | Filter accounts belonging to a specific customer. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Accounts fetched successfully", "data": [ { "id": "3529237a-8ae1-4d63-a6ad-419fae3b9f5e", "account": { "bank_name": "CARBON", "account_name": "Ola Ajayi", "account_number": "6009490194", "balance": 350833340, "available_balance": 350833340, "locked": false }, "owner": { "id": "965c4bf7-e86b-45ca-b286-1ee6f32e991b", "first_name": "Ola", "last_name": "Ajayi", "email": "ola@yahoo.com", "phone": "08071000030" }, "is_static": true, "mode": "sandbox", "created_at": "2024-02-28T01:01:29.000Z", "updated_at": "2024-02-28T01:01:29.000Z" } ], "total": "1" } ``` # Get Transfer Limits Source: https://docs.getcarbon.co/api-reference/accounts/get-transfer-limits GET /v1/accounts/{account_number}/transfer-limits Retrieve the current transfer limits configured for a specific account. ## Overview This endpoint retrieves the current transfer limits configured for the specified account. ### Request **Method:** `GET`\ **URL:** `/v1/accounts/{account_number}/transfer-limits` #### Parameters | Name | In | Type | Required | Description | | ---------------- | ------ | -------- | -------- | --------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `account_number` | Path | `string` | Yes | The account number to fetch limits for. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Transfer limits fetched successfully", "data": { "per_transaction_limit": 500000, "daily_transfer_limit": 2000000 } } ``` > **Note:** All limit values are in the smallest currency unit (kobo for NGN). # Update Transfer Limits Source: https://docs.getcarbon.co/api-reference/accounts/update-transfer-limits PUT /v1/accounts/{account_number}/transfer-limits Update the transfer limits for a specific account. ## Overview This endpoint updates the transfer limits for the specified account. At least one of the limit fields must be provided. ### Request **Method:** `PUT`\ **URL:** `/v1/accounts/{account_number}/transfer-limits` #### Parameters | Name | In | Type | Required | Description | | ---------------- | ------ | -------- | -------- | ---------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `account_number` | Path | `string` | Yes | The account number to update limits for. | #### Request Body | Field | Type | Required | Description | | ----------------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------- | | `per_transaction_limit` | `integer` | No | Maximum amount per single transaction. Must be ≥ 1. | | `daily_transfer_limit` | `integer` | No | Maximum total amount transferable per day. Must be ≥ 1 and cannot be less than `per_transaction_limit`. | At least one field must be provided. If both are provided, `daily_transfer_limit` must be ≥ `per_transaction_limit`. ```json theme={null} { "per_transaction_limit": 500000, "daily_transfer_limit": 2000000 } ``` ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Transfer limits updated successfully", "data": { "per_transaction_limit": 500000, "daily_transfer_limit": 2000000 } } ``` > **Note:** All limit values are in naira. The data shape in successful responses mirrors what the upstream banking service returns and may include additional fields. # Get Banks Source: https://docs.getcarbon.co/api-reference/banks/get-banks GET /v1/banks Retrieve a list of banks and financial institutions ## Overview This endpoint retrieves a list of banks and financial institutions. ### Endpoint ```http theme={null} GET /v1/banks ``` ### Headers ```http theme={null} x-carbon-key: ``` ### Response ```json theme={null} { "status": "success", "message": "banks fetched successfully", "data": [ { "sortCode": "058", "name": "Guarantee Trust Bank" }, { "sortCode": "011", "name": "First Bank" } ] } ``` # Resolve Account Source: https://docs.getcarbon.co/api-reference/banks/resolve-account POST /v1/banks/resolve Resolve account details using the bank code and account number ## Overview This endpoint resolves account details by using the bank code and account number. ### Endpoint ```http theme={null} POST /v1/banks/resolve ``` ### Headers ```http theme={null} x-carbon-key: ``` ### Request Body ```json theme={null} { "number": "1234567890", "code": "058" } ``` ### Response ```json theme={null} { "status": "success", "message": "resolve was successfully", "data": { "accountName": "John Doe", "accountNumber": "1234567890", "bank": { "code": "058", "name": "Guarantee Trust Bank" }, "uptime": 100 } } ``` # Uptime Monitoring Source: https://docs.getcarbon.co/api-reference/banks/uptime GET /v1/banks/uptime Retrieve uptime information for banks. ## Overview This endpoint retrieves the uptime information of banks. The response includes an array of objects, each representing a bank's uptime information. ### Request **Method:** `GET`\ **URL:** `/v1/banks/uptime` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Banks fetched successfully", "data": [ { "cbnBankCode": "100", "nipBankCode": "000022", "uptime": 100 }, { "cbnBankCode": "101", "nipBankCode": "000023", "uptime": 100 }, { "cbnBankCode": "214", "nipBankCode": "000003", "uptime": 100 } ] } ``` # Create Customer Source: https://docs.getcarbon.co/api-reference/customers/create-customer POST /v1/customers Create a new customer with detailed information. ## Overview This endpoint allows you to create a new customer by providing their details such as name, email, phone, and more. ### Request **Method:** `POST`\ **URL:** `/v1/customers` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body ```json theme={null} { "first_name": "Provide first name", "last_name": "Provide last name", "email": "Provide email", "phone": "Provide phone number", "dob": "Provide date of birth", "gender": "Provide gender", "street": "Provide street address", "city": "Provide city", "state": "Provide state", "country": "Provide country", "bvn": "Provide BVN", "nin": "Provide NIN" } ``` ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Customer created successfully", "data": { "id": "e128cc08-a43a-4db3-a27d-64bbcba0a0f2", "first_name": "Ola", "last_name": "Ajayi", "email": "ola@yahoo.com", "phone": "08071000030", "gender": "MALE", "dob": "1990-01-01T00:00:00.000Z", "nin": "11111111111", "bvn": "11111111111", "address": { "street": "NO 1 LAGOS ROAD", "city": "Lagos", "state": "Lagos", "country": "Nigeria" }, "is_business": false, "business_name": null, "mode": "sandbox", "created_at": "2024-03-07T10:12:34.000Z", "updated_at": "2024-03-07T10:12:34.000Z" } } ``` # Fetch Customer Source: https://docs.getcarbon.co/api-reference/customers/get-customer GET /v1/customers/{id} Retrieve details of a specific customer by ID. ## Overview This endpoint retrieves the details of a specific customer identified by their ID. ### Request **Method:** `GET`\ **URL:** `/v1/customers/{id}` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | -------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `id` | Path | `string` | Yes | The unique identifier of the customer. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Customer fetched successfully", "data": { "id": "965c4bf7-e86b-45ca-b286-1ee6f32e991b", "first_name": "Ola", "last_name": "Ajayi", "email": "ola@yahoo.com", "phone": "08071000030", "nin": "11111111111", "bvn": "11111111111", "is_business": false, "business_name": null, "mode": "sandbox", "created_at": "2024-02-27T16:15:38.000Z", "updated_at": "2024-02-27T16:15:38.000Z" } } ``` # Fetch Customers Source: https://docs.getcarbon.co/api-reference/customers/get-customers GET /v1/customers Retrieve a list of customers with pagination and filtering support. ## Overview This endpoint retrieves a list of customers with pagination and optional filtering by `gender`, `email`, `bvn`, or `phone`. ### Request **Method:** `GET` **URL:** `/v1/customers` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | --------- | -------- | ----------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `page` | Query | `integer` | No | Page number for pagination. | | `limit` | Query | `integer` | No | Number of customers per page. | | `gender` | Query | `string` | No | Filter by gender (e.g. `MALE`, `FEMALE`). | | `email` | Query | `string` | No | Filter by customer email address. | | `bvn` | Query | `string` | No | Filter by Bank Verification Number. | | `phone` | Query | `string` | No | Filter by phone number. | ### Response **Status Code:** `200 OK` **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Customers fetched successfully", "data": [ { "id": "1732ca47-42b2-4990-a65d-c369e934eed3", "first_name": "adisa", "last_name": "cooker", "email": "adisa@yahoo.com", "phone": "08071000030", "gender": "MALE", "dob": "1989-12-31T23:00:00.000Z", "address": { "street": "NO 1 LAGOS ROAD", "city": "IKEJA", "state": "LAGOS STATE", "country": "NIGERIA" }, "nin": "11111111111", "bvn": "11111011116", "is_business": false, "business_name": "", "mode": "sandbox", "created_at": "2025-08-19T13:24:12+01:00", "updated_at": "2025-08-19T13:24:12+01:00" } ], "total": 6, "page": 1, "limit": 10, "filters_applied": { "bvn": "11111011116" } } ``` #### Response Fields | Field | Type | Description | | ----------------- | ------- | ---------------------------------- | | `status` | string | Request status (`success`) | | `message` | string | Human-readable result message | | `data` | array | List of customer objects | | `data[].gender` | string | Customer gender (`MALE`, `FEMALE`) | | `data[].dob` | string | Date of birth (ISO 8601) | | `data[].address` | object | Customer address details | | `total` | integer | Total number of matching customers | | `page` | integer | Current page number | | `limit` | integer | Number of results per page | | `filters_applied` | object | Active filters used in the query | # Introduction Source: https://docs.getcarbon.co/api-reference/introduction Overview of the Carbon Business API Welcome to the Carbon Business API documentation. This guide provides all the information you need to integrate and interact with our API. ## Overview The Carbon Business API enables developers and businesses to integrate financial services into their applications seamlessly. With a feature-rich set of endpoints, you can manage accounts, transactions, payouts, and more. ### Key Features * **Account Management**: Create and manage virtual accounts for your customers. * **Transaction Handling**: Retrieve and verify transactions with ease. * **Payouts**: Initiate and track payouts to beneficiaries. * **Banking Services**: Resolve account details and fetch bank information. * **Webhook Notifications**: Receive real-time updates on account and transaction events. ## Next Steps Explore the API endpoints to get started: * [Accounts](./accounts) * [Transactions](./transactions) * [Payouts](./payouts) * [Webhooks](./webhooks) # Accept Offer Source: https://docs.getcarbon.co/api-reference/loans/accept-offer POST /v1/loans/{applicationId}/offer/accept Accept the loan offer on behalf of the customer. ## Overview Accepts the loan offer. The application status transitions to `OFFER_ACCEPTED`. After acceptance, the customer must agree to terms, upload any required post-offer documents, and complete post-offer KYC before disbursement. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/offer/accept` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | No request body required. ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Offer accepted" } ``` #### Error Responses | Status | Message | Cause | | ------ | ----------------------- | ----------------------- | | 400 | `Application not found` | Invalid `applicationId` | # Agree to Terms Source: https://docs.getcarbon.co/api-reference/loans/agree-to-terms POST /v1/loans/{applicationId}/terms/agree Record the customer's agreement to the loan terms of use. ## Overview Records that the customer has accepted the loan terms of use. This must be completed before calling `POST /v1/loans/:applicationId/post-offer-kyc`. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/terms/agree` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | No request body required. ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Terms accepted", "data": {} } ``` #### Error Responses | Status | Message | Cause | | ------ | ---------------------------- | ------------------------------ | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Apply for Loan Source: https://docs.getcarbon.co/api-reference/loans/apply-for-loan POST /v1/loans/apply Submit a loan application for a KYC-verified customer. ## Overview Submits a loan application for a customer whose `kyc_status` is `VERIFIED`. The `reference` field is a merchant-controlled idempotency key — submitting the same reference again returns the existing application with `200 OK` instead of creating a duplicate. All amounts are in **kobo** (1 NGN = 100 kobo). ### Request **Method:** `POST`\ **URL:** `/v1/loans/apply` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body ```json theme={null} { "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "amount": 2000000, "repayment_period": 3, "loan_purpose": "INVENTORY_MGT", "reference": "MERCH_REF_20250115_001" } ``` | Field | Type | Required | Description | | ------------------ | ------ | -------- | --------------------------------------------------- | | `customer_id` | string | Yes | UUID of the KYC-verified customer | | `amount` | number | Yes | Loan amount in **kobo**. Minimum: `300000` (₦3,000) | | `repayment_period` | number | Yes | Repayment duration in months. Range: `1` – `6` | | `loan_purpose` | string | Yes | See enum below | | `reference` | string | Yes | Merchant idempotency key. Max 100 characters. | **`loan_purpose` values:** | Value | Description | | ---------------------- | -------------------- | | `WORKING_CAPITAL` | Working capital | | `EXPANSION_AND_GROWTH` | Business expansion | | `EQUIPMENT_PURCHASE` | Equipment purchase | | `INVENTORY_MGT` | Inventory management | | `DEBT_FINANCING` | Debt financing | | `STARTUP_CAPITAL` | Startup capital | | `OTHERS` | Other purposes | ### Response #### 201 — Application Created ```json theme={null} { "status": "success", "message": "Loan application submitted", "data": { "application_id": "aaaabbbb-cccc-dddd-eeee-ffffffffffff", "status": "PENDING", "amount": 2000000, "repayment_period": 3, "loan_purpose": "INVENTORY_MGT", "created_at": "2025-01-15T10:10:00.000Z" } } ``` #### 200 — Duplicate Reference (Idempotent) ```json theme={null} { "status": "failed", "message": "An application with this reference already exists", "data": { "application_id": "aaaabbbb-cccc-dddd-eeee-ffffffffffff", "status": "PENDING", "amount": 2000000, "repayment_period": 3, "loan_purpose": "INVENTORY_MGT", "created_at": "2025-01-15T10:10:00.000Z" } } ``` #### Error Responses | Status | Message | Cause | | ------ | ---------------------------------------------------------- | -------------------------------- | | 400 | `customer_id is required` | Field missing | | 400 | `Customer not found` | Customer not under this merchant | | 400 | `minimum loan amount is 300000 kobo` | `amount` below minimum | | 400 | `repayment_period must be between 1 and 6 months` | Out of range | | 400 | `loan_purpose must be one of: ...` | Invalid enum value | | 400 | `reference must be 100 characters or less` | `reference` too long | | 422 | `Customer KYC must be verified before applying for a loan` | `kyc_status` is not `VERIFIED` | # Calculate Repayment Source: https://docs.getcarbon.co/api-reference/loans/calculate-repayment POST /v1/loans/calculate-repayment Preview the repayment breakdown for a given loan amount and tenure. ## Overview Returns a projected repayment schedule for a given loan amount, tenure, and state. Can be called at any point — no active application is required. Useful for showing customers a preview before they apply. All amounts are in **kobo**. ### Request **Method:** `POST`\ **URL:** `/v1/loans/calculate-repayment` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body ```json theme={null} { "loan_amount": 2000000, "tenure": 3, "state": "Lagos" } ``` | Field | Type | Required | Description | | ------------- | ------ | -------- | --------------------------------------------- | | `loan_amount` | number | Yes | Loan amount in kobo | | `tenure` | number | Yes | Repayment period in months (positive integer) | | `state` | string | Yes | Nigerian state name (affects rate) | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Repayment calculated", "data": { "monthlyRepayment": 720000, "totalRepayment": 2160000, "interestRate": 8.0, "schedule": [ { "dueDate": "2025-02-15", "principalDue": 666666, "interestDue": 53333, "totalDue": 719999 } ] } } ``` #### Error Responses | Status | Message | Cause | | ------ | -------------------------------------------- | ----------------------- | | 400 | `loan_amount must be a number (in kobo)` | Missing or invalid type | | 400 | `tenure must be a positive integer (months)` | Invalid tenure | | 400 | `state is required` | Missing state | # Charge Repayment Source: https://docs.getcarbon.co/api-reference/loans/charge-repayment POST /v1/loans/{loanId}/repayments Initiate a repayment charge against an active loan. ## Overview Initiates a repayment charge for an active loan. All amounts are in **kobo**. > **Note:** The `:loanId` path parameter is the **loan ID** (field `loan_id` on the application record, populated after disbursement) — not the `application_id`. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:loanId/repayments` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | ------------------------------------------------------ | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `loanId` | Path | `string` | Yes | Loan ID from the application record (`loan_id` field). | #### Request Body ```json theme={null} { "amount": 7199999, "reference": "REPAY_REF_20250215_001" } ``` | Field | Type | Required | Description | | ----------- | ------ | -------- | ----------------------------------------------- | | `amount` | number | Yes | Repayment amount in kobo. Must be positive. | | `reference` | string | Yes | Merchant-supplied reference for this repayment. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Repayment initiated", "data": {} } ``` #### Error Responses | Status | Message | Cause | | ------ | -------------------------------------------- | ---------------------------------------------------- | | 400 | `Loan not found` | `loanId` does not match any loan under this merchant | | 400 | `amount must be a positive number (in kobo)` | `amount` is zero, negative, or not a number | | 400 | `reference is required` | Missing repayment reference | | 422 | `No SME loan ID on record` | Loan not yet registered in lending engine | # Create Guarantor Source: https://docs.getcarbon.co/api-reference/loans/create-guarantor POST /v1/loans/{applicationId}/guarantor Add a guarantor to a loan application. ## Overview Adds a guarantor to the loan. The guarantor will receive an invitation via the `clientPath` link to complete their form. This step is optional unless required by the underwriting decision. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/guarantor` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | #### Request Body ```json theme={null} { "first_name": "Bola", "last_name": "Akin", "phone": "08099887766", "email": "bola.akin@example.com" } ``` | Field | Type | Required | Description | | ------------ | ------ | -------- | --------------------------- | | `first_name` | string | Yes | Guarantor's first name | | `last_name` | string | Yes | Guarantor's last name | | `phone` | string | Yes | Valid Nigerian phone number | | `email` | string | Yes | Valid email address | ### Response #### 201 Created ```json theme={null} { "status": "success", "data": { "id": 7, "firstName": "Bola", "lastName": "Akin", "phoneNumber": "08099887766", "emailAddress": "bola.akin@example.com", "clientPath": "https://app.carbon.ng/guarantor", "loanOfferId": 42, "tokenExpiresAt": "2025-01-22T10:00:00.000Z", "inviteSentAt": null, "submittedAt": null } } ``` | Field | Description | | ---------------- | ------------------------------------------------- | | `clientPath` | Link sent to the guarantor to complete their form | | `tokenExpiresAt` | Expiry of the guarantor invite link | | `submittedAt` | Populated when the guarantor completes the form | #### Error Responses | Status | Message | Cause | | ------ | --------------------------------------- | ------------------------------ | | 400 | `first_name is required` | Field missing | | 400 | `last_name is required` | Field missing | | 400 | `phone must be a valid Nigerian number` | Invalid or missing phone | | 400 | `email must be a valid email address` | Invalid or missing email | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Decline Offer Source: https://docs.getcarbon.co/api-reference/loans/decline-offer POST /v1/loans/{applicationId}/offer/decline Decline the loan offer on behalf of the customer. ## Overview Declines the loan offer. The application status transitions to `OFFER_DECLINED`. A reason must be provided from the allowed enum. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/offer/decline` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | #### Request Body ```json theme={null} { "decline_reason": "HIGH_INTEREST" } ``` | Field | Type | Required | Allowed values | | ---------------- | ------ | -------- | ---------------------------------------------------------------------------- | | `decline_reason` | string | Yes | `HIGH_INTEREST` · `OFFER_SMALL` · `REPAYMENT_PERIOD` · `CHECKING` · `OTHERS` | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Offer declined" } ``` #### Error Responses | Status | Message | Cause | | ------ | ------------------------------------ | ----------------------- | | 400 | `decline_reason is required` | Field missing | | 400 | `decline_reason must be one of: ...` | Invalid enum value | | 400 | `Application not found` | Invalid `applicationId` | # Enroll Customer Source: https://docs.getcarbon.co/api-reference/loans/enroll-customer POST /v1/loans/customers/enroll Enroll an existing customer for lending. Idempotent — returns the existing enrollment if already enrolled. ## Overview Registers an existing customer with the Carbon lending engine. The customer must already exist (created via `POST /v1/customers`) and must have a BVN and an active wallet account. This call is **idempotent** — sending the same `customer_id` twice returns the existing enrollment with `200 OK` instead of creating a duplicate. ### Request **Method:** `POST`\ **URL:** `/v1/loans/customers/enroll` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body ```json theme={null} { "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3" } ``` | Field | Type | Required | Description | | ------------- | ------ | -------- | ---------------------------------------------- | | `customer_id` | string | Yes | UUID of the customer from `POST /v1/customers` | ### Response #### 201 — New Enrollment ```json theme={null} { "status": "success", "message": "Customer enrolled for lending", "data": { "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "customer_type": "INDIVIDUAL", "kyc_status": "PENDING", "enrolled_at": "2025-01-15T10:05:00.000Z" } } ``` #### 200 — Already Enrolled ```json theme={null} { "status": "success", "message": "Customer already enrolled", "data": { "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "account_identifier": "PC_abc123xyz456def78901", "customer_type": "INDIVIDUAL", "kyc_status": "PENDING" } } ``` | Field | Type | Description | | -------------------- | ------ | -------------------------------------------------------- | | `customer_id` | string | The customer UUID | | `account_identifier` | string | Lending engine identifier. Present only on repeat calls. | | `customer_type` | string | `INDIVIDUAL` or `BUSINESS` | | `kyc_status` | string | Initial status is always `PENDING` | | `enrolled_at` | string | UTC ISO-8601 timestamp | #### Error Responses | Status | Message | Cause | | ------ | ------------------------------------------------------------ | ------------------------------------------- | | 400 | `customer_id is required` | Field missing from request body | | 400 | `Customer not found` | `customer_id` not found under this merchant | | 422 | `Customer must have a BVN to enroll for lending` | Customer record has no BVN | | 422 | `Customer must have an account before enrolling for lending` | Customer has no active wallet/account | # Get Active Loan Source: https://docs.getcarbon.co/api-reference/loans/get-active-loan GET /v1/loans/active Fetch the currently active (disbursed) loan for a customer. ## Overview Returns the active disbursed loan for a customer. Use this to check outstanding balance, next repayment date, and current loan status. ### Request **Method:** `GET`\ **URL:** `/v1/loans/active` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `customer_id` | Query | `string` | Yes | UUID of the customer. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Active loan fetched", "data": { "loanId": "LOAN_ABC123", "outstandingBalance": 150000000, "loanStatus": "ACTIVE", "nextRepaymentDate": "2025-02-15", "nextRepaymentAmount": 7199999 } } ``` | `loanStatus` | Meaning | | ------------------- | ----------------------------------- | | `ACTIVE` | Loan is active and in good standing | | `ACTIVE_IN_ARREARS` | Loan is active but overdue | | `CLOSED` | Loan is fully repaid | #### Error Responses | Status | Message | Cause | | ------ | ------------------------------------- | ------------------------------------- | | 400 | `customer_id query param is required` | Missing `customer_id` query parameter | | 400 | `Customer not enrolled for lending` | Customer not found or not enrolled | # Get Bank Statement Status Source: https://docs.getcarbon.co/api-reference/loans/get-bank-statement-status GET /v1/loans/{applicationId}/bank-statement/status Retrieve the status of the most recent bank statement request for a loan application. ## Overview Returns the status of the most recent bank statement request. Poll this after `POST /v1/loans/:applicationId/bank-statement` until the status is `COMPLETED`. ### Request **Method:** `GET`\ **URL:** `/v1/loans/:applicationId/bank-statement/status` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Bank statement status retrieved", "data": { "requestId": "stmt_abc123", "status": "COMPLETED" } } ``` #### Error Responses | Status | Message | Cause | | ------ | ---------------------------- | ------------------------------ | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Get Guarantors Source: https://docs.getcarbon.co/api-reference/loans/get-guarantors GET /v1/loans/{applicationId}/guarantor List all guarantors linked to a loan application. ## Overview Returns all guarantors associated with the loan offer for a given application. ### Request **Method:** `GET`\ **URL:** `/v1/loans/:applicationId/guarantor` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | ### Response #### 200 OK ```json theme={null} { "status": "success", "data": [ { "id": 7, "firstName": "Bola", "lastName": "Akin", "emailAddress": "bola.akin@example.com", "phoneNumber": "08099887766", "clientPath": "https://app.carbon.ng/guarantor", "loanOfferId": 42, "tokenExpiresAt": "2025-01-22T10:00:00.000Z", "inviteSentAt": "2025-01-15T11:00:00.000Z", "submittedAt": null } ] } ``` #### Error Responses | Status | Message | Cause | | ------ | ---------------------------- | ------------------------------ | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Get KYC Status Source: https://docs.getcarbon.co/api-reference/loans/get-kyc-status GET /v1/loans/customers/{customerId}/kyc-status Fetch the current KYC verification status for an enrolled customer. ## Overview Returns the current KYC status for a customer. Poll this endpoint after calling `POST /v1/loans/customers/:customerId/verify-kyc` until `kyc_status` is `VERIFIED`. The customer must be `VERIFIED` before a loan application can be submitted. ### Request **Method:** `GET`\ **URL:** `/v1/loans/customers/:customerId/kyc-status` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `customerId` | Path | `string` | Yes | UUID of the customer. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "KYC status fetched", "data": { "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "kyc_status": "VERIFIED" } } ``` | `kyc_status` | Meaning | | -------------- | --------------------------------------------- | | `NOT_ENROLLED` | Customer has not been enrolled for lending | | `PENDING` | BVN verification in progress | | `VERIFIED` | KYC passed — loan application allowed | | `REJECTED` | KYC failed — customer cannot apply for a loan | #### Error Responses | Status | Message | Cause | | ------ | -------------------- | ---------------------- | | 400 | `Customer not found` | `customerId` not found | # Get Loan Application Source: https://docs.getcarbon.co/api-reference/loans/get-loan GET /v1/loans/{applicationId} Fetch a single loan application. Status is synced live from the lending engine on each call. ## Overview Returns a single loan application. On every call the status is synced from the lending engine, so the `status` field always reflects the current state. Poll this endpoint after starting decisioning until `status` is `HAS_OFFER`. ### Request **Method:** `GET`\ **URL:** `/v1/loans/:applicationId` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Application fetched", "data": { "application_id": "aaaabbbb-cccc-dddd-eeee-ffffffffffff", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "account_identifier": "PC_abc123xyz456def78901", "amount": 2000000, "repayment_period": 3, "loan_purpose": "INVENTORY_MGT", "status": "HAS_OFFER", "offer_status": "PENDING", "loan_status": null, "loan_id": null, "disbursement_account": null, "reference": "MERCH_REF_20250115_001", "created_at": "2025-01-15T10:10:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" } } ``` | Field | Description | | ---------------------- | ------------------------------------------------------------------ | | `application_id` | Your reference for all subsequent loan operations | | `status` | Current application status (see table in List Loans) | | `offer_status` | Offer-level status from the lending engine | | `loan_status` | Active loan status — populated after disbursement | | `loan_id` | Loan ID — populated after disbursement. Used for repayment routes. | | `disbursement_account` | JSONB — set after `POST /disbursement-account` | #### Error Responses | Status | Message | Cause | | ------ | ----------------------- | ----------------------- | | 400 | `Application not found` | Invalid `applicationId` | # Get Loan Offer Source: https://docs.getcarbon.co/api-reference/loans/get-offer GET /v1/loans/{applicationId}/offer Fetch the loan offer details once the application status is HAS_OFFER. ## Overview Returns the loan offer including amount, interest rate, tenure, and repayment schedule. Only available after the application `status` reaches `HAS_OFFER`. ### Request **Method:** `GET`\ **URL:** `/v1/loans/:applicationId/offer` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Offer fetched", "data": { "id": 42, "offerAmount": 200000000, "tenure": 3, "interestRate": 8.0, "repaymentSchedule": [ { "dueDate": "2025-02-15", "principalDue": 6666666, "interestDue": 533333, "totalDue": 7199999 }, { "dueDate": "2025-03-15", "principalDue": 6666666, "interestDue": 480000, "totalDue": 7146666 } ], "offerStatus": "PENDING", "expiresAt": "2025-01-22T10:00:00.000Z" } } ``` | Field | Description | | ------------------- | --------------------------------------------------- | | `offerAmount` | Approved loan amount in kobo | | `tenure` | Repayment period in months | | `interestRate` | Annual interest rate as a percentage | | `repaymentSchedule` | Monthly breakdown of principal, interest, and total | | `offerStatus` | `PENDING` until customer accepts or declines | | `expiresAt` | UTC timestamp when the offer expires | #### Error Responses | Status | Message | Cause | | ------ | ---------------------------- | ------------------------------ | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Get Repayment Schedule Source: https://docs.getcarbon.co/api-reference/loans/get-repayment-schedule GET /v1/loans/{loanId}/repayments Fetch the full repayment schedule for an active loan. ## Overview Returns the complete repayment schedule for an active loan, including due dates, principal, interest, and payment status per instalment. > **Note:** The `:loanId` path parameter is the **loan ID** (field `loan_id` on the application record, populated after disbursement) — not the `application_id`. ### Request **Method:** `GET`\ **URL:** `/v1/loans/:loanId/repayments` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | ------------------------------------------------------ | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `loanId` | Path | `string` | Yes | Loan ID from the application record (`loan_id` field). | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Repayment schedule fetched", "data": { "schedule": [ { "dueDate": "2025-02-15", "principalDue": 6666666, "interestDue": 533333, "totalDue": 7199999, "paidAmount": 0, "status": "UNPAID" }, { "dueDate": "2025-03-15", "principalDue": 6666666, "interestDue": 480000, "totalDue": 7146666, "paidAmount": 0, "status": "UNPAID" } ] } } ``` #### Error Responses | Status | Message | Cause | | ------ | -------------------------- | ---------------------------------------------------- | | 400 | `Loan not found` | `loanId` does not match any loan under this merchant | | 422 | `No SME loan ID on record` | Loan not yet registered in lending engine | # List Loan Applications Source: https://docs.getcarbon.co/api-reference/loans/list-loans GET /v1/loans List all loan applications for the authenticated merchant. ## Overview Returns a paginated list of all loan applications created under the merchant's API key. Supports filtering by status and customer. ### Request **Method:** `GET`\ **URL:** `/v1/loans` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | -------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `status` | Query | `string` | No | Filter by application status. | | `customer_id` | Query | `string` | No | Filter by customer UUID. | | `page` | Query | `number` | No | Page number. Default: `1`. | | `limit` | Query | `number` | No | Results per page. Default: `20`. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Loan applications fetched", "data": [ { "application_id": "aaaabbbb-cccc-dddd-eeee-ffffffffffff", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "amount": 2000000, "repayment_period": 3, "loan_purpose": "INVENTORY_MGT", "status": "HAS_OFFER", "offer_status": "PENDING", "loan_status": null, "loan_id": null, "reference": "MERCH_REF_20250115_001", "created_at": "2025-01-15T10:10:00.000Z", "updated_at": "2025-01-15T10:30:00.000Z" } ], "pagination": { "total": 45, "page": 1, "limit": 20, "pages": 3 } } ``` | `status` value | Meaning | | ----------------------- | ------------------------------------ | | `PENDING` | Awaiting decisioning | | `KYC_PROCESSING` | Identity verification in progress | | `HAS_OFFER` | Offer is ready | | `OFFER_ACCEPTED` | Customer accepted the offer | | `OFFER_DECLINED` | Customer declined the offer | | `OFFER_EXPIRED` | Offer expired before acceptance | | `DECLINED` | Application rejected by underwriting | | `DISBURSEMENT_APPROVED` | Approved, pending disbursement | | `DISBURSED` | Funds disbursed to customer | # List Supported Banks Source: https://docs.getcarbon.co/api-reference/loans/list-mbs-banks GET /v1/loans/banks/list Get the list of banks supported for bank statement requests. ## Overview Returns all banks supported by the Mobile Banking Statement (MBS) service. Use the `bankCode` from this list as `sort_code` when calling `POST /v1/loans/:applicationId/bank-statement`. ### Request **Method:** `GET`\ **URL:** `/v1/loans/banks/list` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | ### Response #### 200 OK ```json theme={null} { "status": "success", "data": [ { "bankCode": "565", "bankName": "Carbon" }, { "bankCode": "058", "bankName": "GTBank" }, { "bankCode": "011", "bankName": "First Bank" }, { "bankCode": "033", "bankName": "UBA" } ] } ``` # Post-Offer KYC Source: https://docs.getcarbon.co/api-reference/loans/post-offer-kyc POST /v1/loans/{applicationId}/post-offer-kyc Complete the post-offer KYC gate. Final step before loan disbursement. ## Overview Completes the post-offer KYC gate and triggers the disbursement eligibility check. All of the following must be true before this call succeeds: * Customer has agreed to terms of use (`POST /terms/agree`) * Offer has been accepted (`POST /offer/accept`) * Board resolution uploaded (non-sole-proprietor businesses only) * Board resolution admin review is not `FAILED` * Guarantor form admin review is not `FAILED` (if a guarantor was added) On success, the `offerStatus` transitions to `POST_OFFER_KYC` and the disbursement eligibility check fires automatically. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/post-offer-kyc` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | No request body required. ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Post-offer KYC saved", "data": {} } ``` #### Error Responses | Status | Message | Cause | | ------ | ---------------------------------------------------------------------------- | -------------------------------------------- | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | | 400 | `Please agree to terms of use before proceeding` | `POST /terms/agree` not yet called | | 400 | `Please accept your loan offer before proceeding` | `POST /offer/accept` not yet called | | 400 | `Please upload your board resolution document before proceeding` | Non-sole-proprietor missing board resolution | | 400 | `Please address the admin review feedback on your board resolution document` | Board resolution admin review failed | | 400 | `Please address the admin review feedback on your guarantor document` | Guarantor form admin review failed | # Request Bank Statement Source: https://docs.getcarbon.co/api-reference/loans/request-bank-statement POST /v1/loans/{applicationId}/bank-statement Request a bank statement for a loan application. ## Overview Requests a bank statement from the customer's bank. For Carbon bank accounts use sort code `565` — no account number or phone required. For all other banks, provide the account number and customer phone. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/bank-statement` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | #### Request Body — Carbon Bank ```json theme={null} { "sort_code": "565" } ``` #### Request Body — External Bank ```json theme={null} { "sort_code": "058", "account_number": "0123456789", "phone": "08012345678" } ``` | Field | Required | Description | | ---------------- | ------------------- | ----------------------------------------------- | | `sort_code` | Yes | 3–6 digit bank sort code. Use `565` for Carbon. | | `account_number` | Yes (external bank) | Exactly 10 digits. | | `phone` | Yes (external bank) | Valid Nigerian phone number. | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Bank statement requested", "data": { "requestId": "stmt_abc123", "status": "PENDING" } } ``` #### Error Responses | Status | Message | Cause | | ------ | ------------------------------------------ | ------------------------------------------- | | 400 | `sort_code is required` | Missing sort code | | 400 | `sort_code must be 3–6 digits` | Invalid format | | 400 | `account_number must be exactly 10 digits` | Non-Carbon bank, missing or invalid account | | 400 | `phone must be a valid Nigerian number` | Non-Carbon bank, missing or invalid phone | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Set Disbursement Account Source: https://docs.getcarbon.co/api-reference/loans/set-disbursement-account POST /v1/loans/{applicationId}/disbursement-account Set the bank account the loan funds will be disbursed to. ## Overview Provides the bank account details for loan disbursement. Call this after the customer receives an offer (`status = HAS_OFFER`). ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/disbursement-account` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | #### Request Body ```json theme={null} { "account_number": "0123456789", "bank_code": "058" } ``` | Field | Type | Required | Description | | ---------------- | ------ | -------- | ---------------------------------- | | `account_number` | string | Yes | Exactly 10 digits | | `bank_code` | string | Yes | Bank sort code — at least 3 digits | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Disbursement account set" } ``` #### Error Responses | Status | Message | Cause | | ------ | ------------------------------------------ | ------------------------------ | | 400 | `account_number must be exactly 10 digits` | Wrong length or non-numeric | | 400 | `bank_code must be at least 3 digits` | Invalid bank code | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Start Decisioning Source: https://docs.getcarbon.co/api-reference/loans/start-decisioning POST /v1/loans/{applicationId}/start-decisioning Trigger the credit decision engine for a loan application. ## Overview Triggers the credit decision engine (Taktile) to evaluate the application. Call this after underwriting data and bank statement have been submitted. In some environments this fires automatically; call it manually if `status` stays `PENDING` after underwriting. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/start-decisioning` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | No request body required. ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Decisioning started", "data": {} } ``` #### Error Responses | Status | Message | Cause | | ------ | ------------------------------------------ | --------------------------------- | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no SME Finance reference` | Application not yet fully created | # Submit Underwriting Data Source: https://docs.getcarbon.co/api-reference/loans/submit-underwriting POST /v1/loans/{applicationId}/submit-underwriting Submit business profile information required for credit decisioning. ## Overview Sends business structure, address, and profile data required by the credit decision engine. Must be called after `POST /v1/loans/apply`. The optional `userIdentity` object provides additional KYC data. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/submit-underwriting` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | ------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID from apply step. | #### Request Body ```json theme={null} { "structure": { "hasWebsite": true, "hasSocialMediaHandles": true, "hasAuditedFinancialStatement": false, "payPension": false, "hasPayeeReceipt": false, "hasBusinessInsurance": false, "hasTaxClearanceCert": false, "hasManagementAccounts": false, "hasAccountant": true, "hasStaffHealthCare": false, "ownProperty": false, "payRent": true }, "address": { "lga": "Lagos Island", "city": "Lagos Island", "state": "LAGOS", "country": "Nigeria", "addressVerificationType": "POWER_BILL" }, "profile": { "yearsInBusiness": "TWO_TO_FIVE", "numberOfLocations": "ONE", "numberOfStaff": "ONE_TO_FIVE", "grossProfitMargin": 35, "operatingExpenses": 150000, "businessRole": "OWNER", "averageDailyCustomers": "FIVE_TO_FOURTEEN", "businessStartDate": "2022-01-15" }, "userIdentity": { "idType": "NIN", "idNumber": "11111111111" } } ``` **`structure`** — all boolean: `hasWebsite` · `hasSocialMediaHandles` · `hasAuditedFinancialStatement` · `payPension` · `hasPayeeReceipt` · `hasBusinessInsurance` · `hasTaxClearanceCert` · `hasManagementAccounts` · `hasAccountant` · `hasStaffHealthCare` · `ownProperty` · `payRent` **`address`:** | Field | Required | Allowed values | | ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------ | | `lga` | Yes | Local government area string | | `city` | No | | | `state` | No | | | `country` | No | | | `addressVerificationType` | No | `POWER_BILL` · `INTERNET_BILL` · `WATER_CORPORATION_BILL` · `WASTE_MANAGEMENT_BILL` · `STAMPED_RENT_RECEIPT` | **`profile`:** | Field | Required | Allowed values | | ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------- | | `yearsInBusiness` | Yes | `ONE` · `TWO_TO_FIVE` · `SIX_OR_MORE` | | `numberOfLocations` | Yes | `ONE` · `TWO_TO_FIVE` · `SIX_OR_MORE` | | `numberOfStaff` | Yes | `ONE_TO_FIVE` · `SIX_TO_FIFTEEN` · `SIXTEEN_OR_MORE` | | `grossProfitMargin` | Yes | Number between `1` and `100` | | `operatingExpenses` | Yes | Positive number (kobo) | | `businessRole` | Yes | `OWNER` · `PARTNER` | | `averageDailyCustomers` | Yes | `ONE_TO_FOUR` · `FIVE_TO_FOURTEEN` · `FIFTEEN_TO_TWENTYFOUR` · `TWENTYFIVE_TO_FORTYNINE` · `FIFTY_OR_MORE` | | `businessStartDate` | Yes | `YYYY-MM-DD` | **`userIdentity`** — optional. Both fields required if the object is included: | Field | Description | | ---------- | ----------------------------------------- | | `idType` | e.g. `NIN`, `DRIVERS_LICENSE`, `PASSPORT` | | `idNumber` | The identity number | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Underwriting data submitted" } ``` #### Error Responses | Status | Message | Cause | | ------ | --------------------------------------------------------------- | ------------------------------------------------ | | 400 | `Application not found` | Invalid `applicationId` | | 400 | `structure is required` | Missing `structure` object | | 400 | `address.lga is required` | Missing `lga` in address | | 400 | `profile.yearsInBusiness must be one of: ...` | Invalid enum | | 400 | `profile.grossProfitMargin must be a number between 1 and 100` | Out of range | | 400 | `userIdentity.idType is required when userIdentity is provided` | Incomplete identity object | | 422 | `Application has no loan ID` | Application not yet registered in lending engine | # Upload Board Resolution Source: https://docs.getcarbon.co/api-reference/loans/upload-board-resolution POST /v1/loans/{applicationId}/board-resolution Link a board resolution document to a loan application. Required for non-sole-proprietor business customers. ## Overview Links a board resolution document to the loan application. Required for all business customers **except** sole proprietors. Sole proprietors skip this step. **Two-step process:** 1. Upload the file via `POST /v1/loans/:applicationId/documents` with `file_tag = BOARD_RESOLUTION_DOC`. Save the `file_url` returned. 2. Pass that `file_url` to this endpoint. ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/board-resolution` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | #### Request Body ```json theme={null} { "board_resolution_url": "https://storage.carbon.ng/files/abc123def456" } ``` | Field | Type | Required | Description | | ---------------------- | ------ | -------- | --------------------------------------------------------------------------- | | `board_resolution_url` | string | Yes | Valid `https://` URL. Must be the `file_url` returned by `POST /documents`. | ### Response #### 200 OK ```json theme={null} { "status": "success", "data": {} } ``` #### Error Responses | Status | Message | Cause | | ------ | ---------------------------------------------------------- | ----------------------------------------------- | | 400 | `board_resolution_url is required` | Field missing | | 400 | `board_resolution_url must be a valid URL` | Not a valid `https://` URL | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | | 400 | `Offer must be accepted before uploading board resolution` | Accept the offer first | | 400 | `Document not found` | URL does not match any previously uploaded file | # Upload Loan Document Source: https://docs.getcarbon.co/api-reference/loans/upload-document POST /v1/loans/{applicationId}/documents Upload a supporting document for a loan application. One file per request. ## Overview Uploads a single document to the loan application. Send one request per file. Accepted formats are PDF, JPEG, and PNG (max 10 MB). The `file_url` returned in the response is required when calling `POST /v1/loans/:applicationId/board-resolution`. **Content-Type:** `multipart/form-data` ### Request **Method:** `POST`\ **URL:** `/v1/loans/:applicationId/documents` #### Parameters | Name | In | Type | Required | Description | | --------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `applicationId` | Path | `string` | Yes | Application ID. | #### Form Fields | Field | Type | Required | Description | | ---------- | ------ | -------- | ---------------------------------- | | `file` | file | Yes | PDF, JPEG, or PNG. Max **10 MB**. | | `file_tag` | string | Yes | Document category. See enum below. | **`file_tag` values:** | Value | Description | | -------------------------- | ----------------------------------------------------------------------------------------- | | `ADDITIONAL_DOCS` | Miscellaneous supporting documents | | `BANK_STATEMENTS` | Bank statement documents | | `BUSINESS_REG_DOCS` | CAC certificate — unlocks the CAC underwriting stage | | `CAC_FORM7_DOCS` | CAC Form 7 — also unlocks the CAC underwriting stage | | `FIN_ACCT_DOCS` | Financial account documents | | `ID_CARD_DOCS` | Identity card documents | | `PAYEE_PAYMENTS_DOCS` | Payee/PAYE payment receipts | | `PENSION_PAYMENTS_DOCS` | Pension payment records | | `TAX_RETURNS_DOCS` | Tax return documents | | `ADDRESS_VERIFICATION_DOC` | Utility bills or rent receipt for address verification | | `BOARD_RESOLUTION_DOC` | Board resolution document — use `file_url` from this response in `POST /board-resolution` | ### Response #### 200 OK ```json theme={null} { "status": "success", "message": "Loan document uploaded", "data": { "filename": "cac_certificate.pdf", "file_url": "https://storage.carbon.ng/files/abc123def456", "file_tag": "BUSINESS_REG_DOCS", "uploaded": true } } ``` | Field | Description | | ---------- | ---------------------------------------------------------------------------- | | `file_url` | URL of the uploaded file. Save this — required for `POST /board-resolution`. | | `file_tag` | The tag that was submitted | | `uploaded` | `true` on success | #### Error Responses | Status | Message | Cause | | ------ | --------------------------------------------------- | ------------------------------ | | 400 | `file is required` | No file attached | | 400 | `Only PDF and image files (JPEG, PNG) are accepted` | Unsupported file type | | 400 | `File size must not exceed 10 MB` | File too large | | 400 | `file_tag is required` | Missing tag | | 400 | `file_tag must be one of: ...` | Invalid tag value | | 400 | `Application not found` | Invalid `applicationId` | | 422 | `Application has no loan ID` | Application not yet registered | # Verify Customer KYC Source: https://docs.getcarbon.co/api-reference/loans/verify-kyc POST /v1/loans/customers/{customerId}/verify-kyc Trigger BVN/identity verification for an enrolled customer. ## Overview Initiates identity verification (BVN check) for a customer who has been enrolled for lending. The result is asynchronous — poll `GET /v1/loans/customers/:customerId/kyc-status` until `kyc_status` becomes `VERIFIED`. This call is idempotent for already-verified customers and returns immediately with `200`. ### Request **Method:** `POST`\ **URL:** `/v1/loans/customers/:customerId/verify-kyc` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | ------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `customerId` | Path | `string` | Yes | UUID of the customer to verify. | No request body required. ### Response #### 200 — Verification Initiated ```json theme={null} { "status": "success", "message": "KYC verification initiated", "data": { "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "kyc_status": "PENDING" } } ``` #### 200 — Already Verified ```json theme={null} { "status": "success", "message": "KYC already verified", "data": { "kyc_status": "VERIFIED" } } ``` #### Error Responses | Status | Message | Cause | | ------ | -------------------------------------- | ------------------------- | | 400 | `Customer not found` | `customerId` not found | | 422 | `Customer is not enrolled for lending` | Enroll the customer first | # Approve or Decline Payout Source: https://docs.getcarbon.co/api-reference/payouts/approve-payout POST /v1/payouts/approvals/approve Approve or decline a pending payout request using its authorization code. ## Overview This endpoint allows you to approve or decline a pending payout request. You must provide the `authCode` from the pending approval record along with the desired `action`. A `reason` is required when declining. ### Request **Method:** `POST` **URL:** `/v1/payouts/approvals/approve` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body | Field | Type | Required | Description | | ---------- | ------ | --------------------------------- | ---------------------------------------------------------- | | `authCode` | string | Yes | Authorization code of the pending payout (e.g. `TRF-...`). | | `action` | string | Yes | Action to take: `approve` or `decline`. | | `reason` | string | Required if `action` is `decline` | Reason for declining the payout. | ```json theme={null} { "authCode": "TRF-1771341301552SNKED", "action": "approve", "reason": "" } ``` When `action` is `decline`, the `reason` field is required. For `approve`, it can be left as an empty string or omitted. ### Response **Status Code:** `200 OK` **Content-Type:** `application/json` #### Example Response (Approved) ```json theme={null} { "status": "success", "message": "Payout approved successfully" } ``` #### Example Response (Declined) ```json theme={null} { "status": "success", "message": "Payout declined successfully" } ``` ### Error Responses **Status Code:** `400 Bad Request` ```json theme={null} { "status": "failed", "message": "invalid authorization code" } ``` **Status Code:** `422 Unprocessable Entity` Returned when `action` is `decline` but `reason` is missing. ```json theme={null} { "status": "failed", "message": "reason is required when declining a payout" } ``` # Create Payout Source: https://docs.getcarbon.co/api-reference/payouts/create-payout POST /v1/payouts Initiate a payout to a beneficiary account. ## Overview This endpoint allows you to initiate a payout to a beneficiary account by providing the necessary details such as amount, source account, and beneficiary information. Before creating a payout, you must validate the beneficiary bank account using the [Resolve Account](/api-reference/banks/resolve-account) endpoint to ensure the account details are correct. The minimum payout amount is ₦200. ### Request **Method:** `POST`\ **URL:** `/v1/payouts` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body | Field | Type | Required | Description | | ---------------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `amount` | number | Yes | Amount to be paid out in kobo (minimum: 20000 = ₦200) | | `source.account_number` | string | Yes | Source account number for the payout | | `beneficiary.bank_code` | string | Yes | Bank code of the beneficiary's bank | | `beneficiary.bank_name` | string | Yes | Name of the beneficiary's bank | | `beneficiary.account_number` | string | Yes | Beneficiary's account number (must be validated using [/api-reference/banks/resolve-account](/api-reference/banks/resolve-account)) | | `beneficiary.account_name` | string | Yes | Beneficiary's account name (obtained from resolve account response) | | `reference` | string | Yes | Unique reference for this payout request (max 30 characters) | | `meta_data` | object | No | Additional metadata for the transaction | | `remark` | string | Yes | Description or note for the payout | The `reference` field must be unique for each new payout request. Using a duplicate reference will result in an error. **Example Reference Formats:** * `PAYOUT_USER123_20260119` * `PAY_INV_456789` * `SALARY_EMP001_JAN26` ```json theme={null} { "amount": 15000, "source": { "account_number": "1234567890" }, "beneficiary": { "bank_code": "565", "bank_name": "CARBON", "account_number": "9876543210", "account_name": "John Doe" }, "reference": "PAYOUT_USER123_20260119", "meta_data": {}, "remark": "Payment for services" } ``` ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Payout was initiated successfully", "data": { "amount": 15000, "total": 15000, "fee": 0, "reference": "PAYOUT_USER123_20260119", "beneficiary": { "bank_code": "565", "bank_name": "CARBON", "account_number": "9876543210", "account_name": "John Doe" } } } ``` # Fetch Payout Beneficiaries Source: https://docs.getcarbon.co/api-reference/payouts/get-payout-beneficiaries GET /v1/payouts/beneficiaries Retrieve a paginated list of unique payout beneficiaries for an account based on previous payout transactions. ## Overview The Fetch Payout Beneficiaries endpoint retrieves a deduplicated list of all unique beneficiaries that have received payouts from your account. This is useful for displaying a list of frequent recipients when creating new payouts. ## Usage Examples ### Basic Usage Fetch the first page of beneficiaries with default limit: ```bash theme={null} GET /v1/payouts/beneficiaries ``` ### Pagination Fetch a specific page with custom limit: ```bash theme={null} GET /v1/payouts/beneficiaries?page=2&limit=50 ``` ### Search Search for beneficiaries by account name or number: ```bash theme={null} GET /v1/payouts/beneficiaries?search=john ``` ### Combined Parameters Use multiple parameters together: ```bash theme={null} GET /v1/payouts/beneficiaries?page=1&limit=10&search=access ``` ## Error Handling | Status Code | Description | | ----------- | ----------------------------------------------- | | 200 | Success - Beneficiaries retrieved successfully | | 400 | Bad Request - Invalid query parameters | | 401 | Unauthorized - Missing or invalid API key | | 500 | Internal Server Error - Unexpected server error | ## Notes * The endpoint returns deduplicated beneficiaries based on unique combinations of bank\_code and account\_number * Search functionality is case-insensitive and matches partial strings in both account\_number and account\_name fields * If no beneficiaries are found, an empty array will be returned in the data field * The pagination object always reflects the current state, even when no results are found # Fetch Payout Status Source: https://docs.getcarbon.co/api-reference/payouts/get-payout-status GET /v1/payouts/status/{reference} Retrieve the status of a specific payout by reference. ## Overview This endpoint retrieves the status of a specific payout using the provided reference. ### Request **Method:** `GET`\ **URL:** `/v1/payouts/status/{reference}` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | ----------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `reference` | Path | `string` | Yes | The unique reference of the payout. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "payout fetched successfully", "data": { "status": "SUCCESS", "message": "STAGING Env Success | Transfer in progress - null", "amount": 150, "total": 175, "fee": 25, "reference": "121324-43554-65656-67676-00-00010010", "uniqueRef": "204041344312337", "beneficiary": { "bankCode": "057", "accountNumber": "00000000", "accountName": "ALex Oyo", "bankName": "Zenith Bank" } } } ``` # Fetch Payout Transaction Source: https://docs.getcarbon.co/api-reference/payouts/get-payout-transaction GET /v1/payouts/transaction/{reference} Retrieve details of a specific payout transaction by reference. ## Overview This endpoint retrieves the details of a specific payout transaction using the provided reference. ### Request **Method:** `GET`\ **URL:** `/v1/payouts/transaction/{reference}` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | ----------------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `reference` | Path | `string` | Yes | The unique reference of the payout transaction. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "payout fetched successfully", "data": { "status": "SUCCESS", "message": "STAGING Env Success | Transfer in progress - null", "amount": 150, "total": 150, "fee": 25, "reference": "121324-43554-65656-67676-00-00010010", "uniqueRef": "204041344312337", "providerReference": null, "beneficiary": { "bankCode": "057", "accountNumber": "00000000", "accountName": "ALex Oyo", "bankName": "Zenith Bank" } } } ``` # Get Pending Payout Approvals Source: https://docs.getcarbon.co/api-reference/payouts/get-pending-payout-approvals GET /v1/payouts/approvals This endpoint retrieves a list of all payout requests that are pending approval. ## Overview This endpoint allows you to retrieve a list of all payout requests that are currently pending approval. You can also include expired approval requests in the results. ### Request **Method:** `GET`\ **URL:** `/v1/payouts/approvals` #### Parameters | Name | In | Type | Required | Description | | ----------------- | ------ | --------- | -------- | ------------------------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `include_expired` | Query | `boolean` | No | Set to `true` to include expired approvals. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` ```json theme={null} { "status": "success", "message": "Payout approvals fetched successfully", "data": [ { "id": 3, "authorization_code": "TRF-1771322171170DVXV8", "status": "pending", "amount": 20000, "beneficiary": { "account_name": "DOE JOHN", "account_number": "9002118329", "bank_name": null, "bank_code": "305" }, "client_id": "551210620", "narration": "TRF/DOE JOHN/9002118329/TEST - 100", "currency": "NGN", "expires_at": "2026-02-17T10:26:19.000000Z", "approved_by": null, "approved_at": null, "rejection_reason": null, "created_at": "2026-02-17T09:56:19.000000Z", "updated_at": "2026-02-17T09:56:19.000000Z", "is_expired": true, "is_pending": false }, { "id": 2, "authorization_code": "TRF-1771322053739RY1V2", "status": "pending", "amount": 20000, "beneficiary": { "account_name": "DOE JOHN", "account_number": "9002118329", "bank_name": null, "bank_code": "305" }, "client_id": "551210620", "narration": "TRF/DOE JOHN/9002118329/TEST - 100", "currency": "NGN", "expires_at": "2026-02-17T10:24:21.000000Z", "approved_by": null, "approved_at": null, "rejection_reason": null, "created_at": "2026-02-17T09:54:21.000000Z", "updated_at": "2026-02-17T09:54:21.000000Z", "is_expired": true, "is_pending": false }, { "id": 1, "authorization_code": "TRF-1771321717028NT4TF", "status": "pending", "amount": 20000, "beneficiary": { "account_name": "DOE JOHN", "account_number": "9002118329", "bank_name": null, "bank_code": "305" }, "client_id": "551210620", "narration": "TRF/DOE JOHN/9002118329/TEST - 100", "currency": "NGN", "expires_at": "2026-02-17T10:18:44.000000Z", "approved_by": null, "approved_at": null, "rejection_reason": null, "created_at": "2026-02-17T09:48:44.000000Z", "updated_at": "2026-02-17T09:48:44.000000Z", "is_expired": true, "is_pending": false } ] } ``` # Merchant Fee Charge Source: https://docs.getcarbon.co/api-reference/payouts/merchant-fee-charge POST /v1/payouts/merchant-fee-charge Process a merchant fee charge by debiting a source account and crediting a target account. ## Overview This endpoint processes a merchant fee charge by debiting a source account and crediting a target account for the specified amount. ### Request **Method:** `POST` **URL:** `/v1/payouts/merchant-fee-charge` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | -------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | #### Request Body | Field | Type | Required | Description | | ----------------- | ------ | -------- | -------------------------------------------------- | | `amount` | number | Yes | Amount in Naira. Must be a positive value. | | `sourceAccountId` | string | Yes | Source account number to be debited. | | `targetAccountId` | string | Yes | Target account number to be credited. | | `description` | string | No | Optional narration/description for the fee charge. | ```json theme={null} { "amount": 10, "description": "Fee Charge of N100", "sourceAccountId": "0340899287", "targetAccountId": "6009490194" } ``` ### Response **Status Code:** `201 Created` **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "Merchant fee charge processed successful...", "data": { "success": true, "message": "Successful", "statusCode": "00", "transaction": { "wasReversed": false, "amount": 1000, "description": "Fee Charge of N100", "category": "Fund Transfer", "uniqueRef": "177383661255062352", "internalRef": "1406812", "transactionType": "DEBIT", "entryDate": "2026-03-18T12:23:41.015+0000", "internal": true, "accountId": "0340899287" } } } ``` #### Response Fields | Field | Type | Description | | ---------------------------------- | ------- | ------------------------------------------------- | | `status` | string | Overall request status (`success`) | | `message` | string | Human-readable result message | | `data.success` | boolean | Whether the fee charge was processed successfully | | `data.statusCode` | string | Business status code (`00` = success) | | `data.transaction.uniqueRef` | string | Unique transaction reference for reconciliation | | `data.transaction.internalRef` | string | Internal reference for support lookups | | `data.transaction.amount` | number | Amount processed (in kobo) | | `data.transaction.transactionType` | string | Direction of the transaction (`DEBIT`) | | `data.transaction.accountId` | string | The source account that was debited | `data.statusCode: "00"` indicates a successful transaction. Use `data.transaction.uniqueRef` and `data.transaction.internalRef` for reconciliation and support. ### Error Responses **Status Code:** `400 Bad Request` A 400 response indicates the request was invalid — for example, missing required fields, invalid account identifiers, or an invalid amount. ```json theme={null} { "status": "failed", "message": "invalid source account" } ``` # Fetch Transactions Source: https://docs.getcarbon.co/api-reference/transactions/fetch-transactions GET /v1/accounts/{account_number}/transactions Retrieve a list of transactions for a specific account. ## Overview This endpoint retrieves a list of transactions for a specific account. ### HTTP Request **GET** `/v1/accounts/{account_number}/transactions` ### Parameters * **Header**: `x-carbon-key` (string, required) - API key for authentication. * **Path**: * `account_number` (integer, required) - The account number. * **Query**: * `page` (integer, optional, default=1) - The page number for paginated results. * `limit` (integer, optional, default=10) - The number of transactions per page. ### Example Response ```json theme={null} { "status": "success", "message": "transaction fetched successfully", "data": { "transactions": [ { "wasReversed": false, "amount": 0, "description": "", "category": "", "uniqueRef": "", "internalRef": "", "transactionType": "", "entryDate": "", "updatedDate": "", "internal": true, "accountId": "", "externalStatus": "", "balance": 0 } ], "last": true, "query": { "limit": 10, "offset": 0, "includeBalance": true, "status": [""], "entity": "" } } } ``` # Verify Transaction Source: https://docs.getcarbon.co/api-reference/transactions/verify-transaction GET /v1/accounts/{account_number}/transactions/{reference} Retrieve transaction details for a specific account and reference. ## Overview This endpoint retrieves the transaction details for a specific account and reference. ### HTTP Request **GET** `/v1/accounts/{account_number}/transactions/{reference}` ### Parameters * **Header**: `x-carbon-key` (string, required) - API key for authentication. * **Path**: * `account_number` (integer, required) - The account number. * `reference` (integer, required) - The transaction reference. ### Example Response ```json theme={null} { "status": "success", "message": "transaction fetched successfully", "data": { "success": true, "message": "Successful", "statusCode": "00", "transaction": { "wasReversed": false, "amount": 0, "description": "", "category": "", "uniqueRef": "", "internalRef": "", "transactionType": "", "entryDate": "", "updatedDate": "", "internal": true, "accountId": "", "externalStatus": "" } } } ``` # Webhook Events Source: https://docs.getcarbon.co/api-reference/webhooks/get-webhook-history GET /v1/webhook/history Retrieve the history of webhook events with pagination. ## Overview This endpoint retrieves the history of webhook events with pagination. ### Request **Method:** `GET`\ **URL:** `/v1/webhook/history` #### Parameters | Name | In | Type | Required | Description | | -------------- | ------ | --------- | -------- | --------------------------- | | `x-carbon-key` | Header | `string` | Yes | API key for authentication. | | `page` | Query | `integer` | No | Page number for pagination. | | `limit` | Query | `integer` | No | Number of events per page. | ### Response **Status Code:** `200 OK`\ **Content-Type:** `application/json` #### Example Response ```json theme={null} { "status": "success", "message": "data fetched successfully", "data": [ { "event_id": "REF-300102", "event_type": "account.incoming-transaction", "data": { "event": "account.incoming-transaction", "data": { "id": "fd2a1c7e-a17b-477b-8402-734967498369", "amount": 100000, "currency": "NGN", "transactionType": "CREDIT", "entryDate": "2024-12-16T13:51:52.759+0000", "uniqueRef": "REF-300102", "account": { "id": "a9a27820-c0d0-4e26-95e1-7625888e9fb9", "bankAccount": { "accountName": "CARBON BUSINESS DEMO - BRYANA HUELS", "accountNumber": "0000000000", "bank": { "code": "565", "name": "CARBON" } }, "static": true, "currency": "NGN", "clientId": "251170382" } } }, "created_at": "2024-12-16T15:06:13.000Z", "updated_at": "2024-12-16T15:06:13.000Z" }, { "event_id": "REF-300102_CBN_STAMP_DUTY_CHARGE", "event_type": "account.outgoing-transaction", "data": { "event": "account.outgoing-transaction", "data": { "id": "d66a39f2-692c-4d19-8098-6bc45fd9f6bf", "amount": 50, "currency": "NGN", "transactionType": "DEBIT", "entryDate": "2024-12-16T13:51:55.156+0000", "uniqueRef": "REF-300102_CBN_STAMP_DUTY_CHARGE", "account": { "id": "a9a27820-c0d0-4e26-95e1-7625888e9fb9", "bankAccount": { "accountName": "CARBON BUSINESS DEMO - BRYANA HUELS", "accountNumber": "0000000000", "bank": { "code": "565", "name": "CARBON" } }, "static": true, "currency": "NGN", "clientId": "251170382" } } }, "created_at": "2024-12-16T14:52:15.000Z", "updated_at": "2024-12-16T14:52:15.000Z" } ], "total": 20 } ``` # Resend WebHook Event Source: https://docs.getcarbon.co/api-reference/webhooks/resend-webhook-event POST /v1/webhook/resend-webhook-event Resend a webhook event to the specified endpoint. ## Overview This endpoint allows you to resend a webhook event to the specified endpoint. ## Request ### Headers * `x-carbon-key` (string, required): Your API key. ### Body ```json theme={null} { "event_id": "string" } ``` * `event_id` (string, required): The ID of the event to be resent. ## Response ### Example Response ```json theme={null} { "status": "success", "message": "Webhook event resent successfully", "data": {} } ``` ## Notes * Ensure the `event_id` is valid and corresponds to an existing webhook event. # Authentication Source: https://docs.getcarbon.co/authentication Learn how to authenticate with the Carbon Business API. The Carbon Business API uses API key-based authentication. Every request to the API must include the `apikey`and `x-carbon-key` headers with your API key. ## Authentication Methods ### `apikey` The `apikey` is a security scheme used to authenticate requests. It must be included in the request header as `apikey`. This key is provided by the Carbon Integration team and is required for all API interactions. ### `x-carbon-key` The `x-carbon-key` is the header where your app API key is passed. It is generated via the developer page on Carbon Business. It is mandatory for every request to the Carbon Business API. Without this header, the API will reject the request with an authentication error. *** ## How to Get Your API Key 1. Get in touch with our [team via email: sme@getcarbon.co](mailto:sme@getcarbon.co) 2. Sign Up via the sandbox environment [Carbon Business developer portal](https://carbon-business-client-staging.getcarbon.co/). 3. Navigate to the Developer menu, Top right Dropdown menu > Developer. 4. Generate a new CARBON\_API\_KEY > or use an existing one. *** developer ## Example Include the `apikey` header in your requests: ```http theme={null} apikey: YOUR_API_KEY ``` Include the `x-carbon-key` header in your requests: ```http theme={null} x-carbon-key: CARBON_API_KEY ``` ### Example Request ```http theme={null} GET /v1/accounts HTTP/1.1 Host: {{BASE_URL}} apikey: YOUR_API_KEY x-carbon-key: CARBON_API_KEY ``` *** ## Environments | Environment | URL | | ----------- | ------------------------------------------------------------------------------------------------------------ | | Live | \[Production URL] | | Sandbox | [https://carbonapistagingsecure.getcarbon.co/baas/api](https://carbonapistagingsecure.getcarbon.co/baas/api) | # Environments Source: https://docs.getcarbon.co/environment Understand the different environments available in Carbon Business API for development and deployment. When integrating with the Carbon Business API, understanding the different environments available is crucial for smooth development and deployment. Carbon provides two distinct environments (Sandbox and Live) each tailored for specific stages of your integration process. This guide will walk you through the purpose of each environment, how to use them effectively, and best practices for transitioning from testing to live operations. ## Sandbox Environment ### What is the Sandbox Environment? The Sandbox environment is a tailored testing ground that allows you to interact with the API without affecting live data or moving real funds. It is designed to mimic the Live environment's functionality. ### Why Use the Sandbox Environment? * **Safe Testing:** Experiment with API calls without financial risk. * **Development:** Integrate and debug your application freely. * **Simulation:** Replicate various transaction scenarios and server responses. ### How to Access the Sandbox Environment You can interact with the Sandbox environment using the following base URL: ```bash theme={null} https://carbonapistagingsecure.getcarbon.co/baas/api ``` Ensure you use the API keys generated from your Sandbox Dashboard to avoid authentication issues. * Treat the Sandbox as a replica of your production environment. * Test your error handling and edge cases. * Frequent your dashboard to verify the state of your simulated transactions. ## Live Environment ### What is the Live Environment? The Live environment is the production setup where real-world transactions take place. Connections made here interact with actual banking networks, and money movements are real and final. ### Why Use the Live Environment? * **Real Operations:** Process genuine customer transactions and payouts. * **Production Data:** Maintain actual records of accounts and financial history. ### How to Access the Live Environment The Live environment URL is provided upon completing your business verification and Go-Live process. You must switch your API keys to the Live versions. * **Security:** Tightly control access to your Live API keys. * **Monitoring:** Set up logging and alerts for your API integrations. * **Compliance:** Ensure your integration adheres to all financial regulations and standards. ## Transitioning from Sandbox to Live Perform a final round of testing in the Sandbox to ensure all functionality works as intended. Modify your application’s configuration to point to the Live environment URL. Ensure that `apikey`, `x-carbon-key`, and environment-specific settings are updated accordingly. Consider rolling out your integration gradually in Live to monitor its performance and make adjustments as needed. After moving to Live, continue to monitor your integration closely, particularly in the early stages of deployment. ## Important Considerations Remember that your API keys are environment-specific. Ensure that you are using the correct keys for each environment to avoid authentication errors. Always store your API keys securely and follow best practices for API security, especially in the Live environment. Keep in mind that data in the Sandbox is separate from Live. Actions in the Sandbox will not affect your live data or operations. # Error Handling Source: https://docs.getcarbon.co/error We use the conventional HTTP response codes to indicate the success or failure of an API request. Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted, etc.). Codes in the 5xx range indicate an error with our servers. ## HTTP Status Codes | Status Code | Description | | ----------- | ------------------------------------------- | | 200 | OK - Request was successful | | 201 | Created - Resource was successfully created | | Status Code | Description | | ----------- | ------------------------------------------------------------------------------------- | | 400 | Bad Request - The request was unacceptable, often due to missing a required parameter | | 401 | Unauthorized - No valid API key was provided | | 402 | Request Failed - The parameters were valid but the request failed | | 403 | Forbidden - The API key doesn't have permission to perform the request | | 404 | Not Found - The requested resource doesn't exist | | 429 | Too Many Requests - Too many requests hit the API | | Status Code | Description | | ----------- | ------------------------------------------------------------------- | | 500 | Internal Server Error - Something went wrong on Carbon's end | | 502 | Bad Gateway - Invalid response from an upstream server | | 503 | Service Unavailable - Carbon is temporarily offline for maintenance | | 504 | Gateway Timeout - The server didn't respond in time | ## Error Response Format All errors return JSON in the following format: ```json theme={null} { "status": "error", "message": "A descriptive error message", "data": { "error_code": "SPECIFIC_ERROR_CODE", "details": "Additional error details if available" } } ``` ## Common Error Examples ```json Authentication Error (401) theme={null} { "status": "error", "message": "Unauthorized access", "data": { "error_code": "INVALID_API_KEY", "details": "The provided API key is invalid or expired" } } ``` ```json Validation Error (400) theme={null} { "status": "error", "message": "Validation failed", "data": { "error_code": "MISSING_REQUIRED_FIELD", "details": "The 'customer_id' field is required" } } ``` ```json Rate Limit Error (429) theme={null} { "status": "error", "message": "Rate limit exceeded", "data": { "error_code": "RATE_LIMIT_EXCEEDED", "details": "You have exceeded the rate limit. Please wait before making another request." } } ``` ## Best Practices **Always Handle Errors Gracefully** Implement proper error handling in your application to ensure a good user experience when API calls fail. **Retry Logic** For 5xx errors, implement exponential backoff retry logic as these are typically temporary issues. **Log Error Details** Always log the full error response for debugging purposes, but never expose sensitive error details to end users. # Introduction Source: https://docs.getcarbon.co/introduction Learn about the Carbon Business API, its features, and how it enables seamless integration of financial services into your applications. Hero Light Hero Dark # Carbon Business API Welcome to the Carbon Business API documentation. This API enables businesses to integrate financial services into their applications with ease. ## Overview The Carbon Business API enables developers and businesses to integrate financial services into their applications seamlessly. With a feature-rich set of endpoints, you can manage accounts, transactions, payouts, and more. ### Key Features Create and manage virtual accounts for your customers with dynamic or static account numbers. Retrieve and verify transactions with real-time status updates and comprehensive filtering. Initiate and track payouts to beneficiaries with detailed transaction monitoring. Resolve account details and fetch comprehensive bank information across Nigeria. Receive real-time updates on account and transaction events for immediate processing. Comprehensive customer lifecycle management with KYC and business verification. ## Getting Started Sign up for a Carbon Business account and get access to the developer dashboard. Generate your API keys from the developer portal for both Sandbox and Live environments. Start with our quickstart guide to make your first API call and create a virtual account. Configure webhook endpoints to receive real-time notifications about transactions. ## Use Cases Accept payments through virtual accounts, handle refunds, and manage customer transactions seamlessly. Build digital wallets, payment processing systems, and financial management tools. Integrate payment collection for subscriptions, handle marketplace transactions, and automate payouts. Manage multi-party transactions, escrow services, and automated commission distributions. ## Quick Example ```javascript JavaScript theme={null} const response = await fetch('https://carbonapistagingsecure.getcarbon.co/baas/api/v1/accounts', { method: 'POST', headers: { 'apikey': 'your-api-key', 'x-carbon-key': 'your-carbon-key', 'Content-Type': 'application/json' }, body: JSON.stringify({ account_type: 'static', third_party: true, customer_id: 'customer-uuid' }) }); const account = await response.json(); console.log('Account created:', account); ``` ```python Python theme={null} import requests url = 'https://carbonapistagingsecure.getcarbon.co/baas/api/v1/accounts' headers = { 'apikey': 'your-api-key', 'x-carbon-key': 'your-carbon-key', 'Content-Type': 'application/json' } data = { 'account_type': 'static', 'third_party': True, 'customer_id': 'customer-uuid' } response = requests.post(url, headers=headers, json=data) account = response.json() print('Account created:', account) ``` ```curl cURL theme={null} curl -X POST https://carbonapistagingsecure.getcarbon.co/baas/api/v1/accounts \ -H "apikey: your-api-key" \ -H "x-carbon-key: your-carbon-key" \ -H "Content-Type: application/json" \ -d '{ "account_type": "static", "third_party": true, "customer_id": "customer-uuid" }' ``` ## API Specifications **Base URL:** `https://carbonapistagingsecure.getcarbon.co/baas/api` (Sandbox)\ **Authentication:** API Key + x-carbon-key headers\ **Response Format:** JSON\ **Rate Limits:** Contact support for current limits ## Ready to Get Started? Jump into our quickstart guide to create your first virtual account and start processing transactions. Explore our comprehensive API documentation with detailed endpoint specifications. # Lending Source: https://docs.getcarbon.co/lending Enable your customers to access business loans through the Carbon Lending API. ## Overview The Carbon Lending API allows fintech partners to originate business loans for their own end-customers. The full flow — from customer enrollment through repayment — is orchestrated via REST API with webhook notifications at every lifecycle event. ## Flow ``` Customer ────► Account ────► Enroll ────► KYC ────► Apply ────► Underwriting │ ▼ Repayment ◄──── Disburse ◄──── Post-offer KYC ◄──── Offer ``` ### Step-by-step | Step | Action | Endpoint | | ---- | ---------------------------------------- | ----------------------------------------- | | 0 | Create the customer record | `POST /v1/customers` | | 1 | Create a Carbon account for the customer | `POST /v1/accounts` | | 2 | Enroll customer for lending | `POST /v1/loans/customers/enroll` | | 3 | Trigger KYC verification | `POST /v1/loans/customers/:id/verify-kyc` | | 4 | Poll KYC until `VERIFIED` | `GET /v1/loans/customers/:id/kyc-status` | | 5 | Submit loan application | `POST /v1/loans/apply` | | 6 | Submit business profile data | `POST /v1/loans/:id/submit-underwriting` | | 7 | Request bank statement | `POST /v1/loans/:id/bank-statement` | | 8 | Upload supporting documents | `POST /v1/loans/:id/documents` | | 9 | Start credit decisioning | `POST /v1/loans/:id/start-decisioning` | | 10 | Poll until `HAS_OFFER` | `GET /v1/loans/:id` | | 11 | Fetch offer details | `GET /v1/loans/:id/offer` | | 12 | Set disbursement account | `POST /v1/loans/:id/disbursement-account` | | 13 | Accept offer | `POST /v1/loans/:id/offer/accept` | | 14 | Agree to terms | `POST /v1/loans/:id/terms/agree` | | 15 | Upload board resolution (non-sole-prop) | `POST /v1/loans/:id/board-resolution` | | 16 | Add guarantor (if required) | `POST /v1/loans/:id/guarantor` | | 17 | Complete post-offer KYC | `POST /v1/loans/:id/post-offer-kyc` | | — | Carbon admin disburses | *(internal)* | | 18 | Charge repayments | `POST /v1/loans/:loanId/repayments` | *** ## Key Concepts ### Amounts All monetary values are in **kobo**. Divide by 100 to get Naira. ``` 300,000 kobo = ₦3,000 ``` ### `application_id` vs `loan_id` * `application_id` — UUID created when `POST /v1/loans/apply` succeeds. Used in all loan operation routes (`:applicationId`). * `loan_id` — Populated **after disbursement**. Used only for repayment routes (`:loanId`). ### Idempotency | Endpoint | Key | | --------------------------------- | ------------- | | `POST /v1/loans/customers/enroll` | `customer_id` | | `POST /v1/loans/apply` | `reference` | Resending the same key returns the existing record rather than creating a duplicate. ### Board Resolution Required for all business customers **except** sole proprietors. Two-step process: 1. Upload file via `POST /v1/loans/:id/documents` with `file_tag = BOARD_RESOLUTION_DOC` — save the `file_url`. 2. Pass `file_url` to `POST /v1/loans/:id/board-resolution`. *** ## Webhooks Subscribe to real-time loan events via the webhook system. Each status change fires an event to your registered webhook URL. | Event | Trigger | | --------------------------- | ------------------------- | | `loan.application.received` | Application submitted | | `loan.kyc.pending` | KYC verification started | | `loan.offer.generated` | Offer ready | | `loan.offer.accepted` | Offer accepted | | `loan.offer.declined` | Offer declined | | `loan.offer.expired` | Offer expired | | `loan.application.declined` | Application rejected | | `loan.application.approved` | Approved for disbursement | | `loan.disbursed` | Funds disbursed | | `loan.arrears` | Loan in arrears | | `loan.closed` | Loan fully repaid | See the [Webhooks guide](/webhooks/introduction) for delivery details and retry behaviour. # SDKs & Libraries Source: https://docs.getcarbon.co/libraries Integrate Carbon Business API effortlessly using our official SDKs and libraries for various programming languages. The Carbon Business API SDKs provide a convenient way to integrate with our platform, offering pre-built functions for common operations like account creation, payouts, and transaction management. ## Node.js/JavaScript SDK Our official Node.js SDK simplifies integration with the Carbon Business API, providing TypeScript support and comprehensive error handling. ### Installation Install the SDK via npm: ```bash theme={null} npm install carbon-baas-sdk ``` Requires Node.js 14.0 or higher for optimal compatibility. ### Configuration Initialize the SDK with your API key and environment mode. Always use environment variables to store your API keys securely. Never hardcode them in your source code. #### Environment Setup ```bash theme={null} # .env file CARBON_API_KEY=your_api_key_here CARBON_ENV=sandbox # or 'live' for production ``` #### CommonJS Example ```javascript theme={null} const carbon = require('carbon-baas-sdk'); // Initialize the SDK carbon.initialize(process.env.CARBON_API_KEY, process.env.CARBON_ENV); ``` #### ES6/TypeScript Example ```typescript theme={null} import { CarbonSDK } from 'carbon-baas-sdk'; // Initialize the SDK const carbon = new CarbonSDK({ apiKey: process.env.CARBON_API_KEY!, environment: process.env.CARBON_ENV as 'sandbox' | 'live' }); ``` ## Core Operations ### Account Management ```javascript theme={null} // Create a collection sub-account const collectionAccount = await carbon.createAccount({ accountType: 'static', thirdParty: false, accountName: 'My Collections' }); console.log('Account Number:', collectionAccount.data.account.account_number); ``` ```javascript theme={null} // First create a customer const customer = await carbon.createCustomer({ email: 'customer@example.com', phone: '08012345678', firstName: 'John', lastName: 'Doe', bvn: '12345678901' }); // Then create account linked to customer const businessAccount = await carbon.createAccount({ accountType: 'static', thirdParty: true, customerId: customer.data.id }); ``` ```javascript theme={null} try { const account = await carbon.getAccount('1234567890'); console.log('Account Details:', account.data); } catch (error) { console.error('Account not found:', error.message); } ``` ```javascript theme={null} const balance = await carbon.fetchBalance('1234567890'); console.log('Available Balance:', balance.data.available_balance); ``` ### Payouts ```javascript theme={null} const validation = await carbon.resolveAccount({ accountNumber: '0123456789', bankCode: '058' }); if (validation.status === 'success') { console.log('Account Name:', validation.data.account_name); } ``` ```javascript theme={null} const payout = await carbon.createPayout({ amount: 50000, // Amount in kobo (₦500) source: { accountNumber: 'your_account_number' }, beneficiary: { bankCode: '058', bankName: 'GTBank', accountNumber: '0123456789', accountName: 'Jane Smith' }, reference: `PAY_${Date.now()}`, // Unique reference remark: 'Service payment' }); console.log('Payout Reference:', payout.data.reference); ``` ```javascript theme={null} const status = await carbon.getPayoutStatus('PAY_1737326400123'); console.log('Payout Status:', status.data.status); ``` ### Transaction Management ```javascript theme={null} const transactions = await carbon.fetchTransactions({ accountNumber: '1234567890', startDate: '2026-01-01', endDate: '2026-01-19', limit: 50 }); console.log('Transaction Count:', transactions.data.length); ``` ```javascript theme={null} const verification = await carbon.verifyTransaction('TXN_REF_123'); if (verification.data.status === 'successful') { console.log('Transaction verified successfully'); } ``` ## Error Handling Implement robust error handling for production applications: ```javascript theme={null} try { const result = await carbon.createPayout(payoutData); return result; } catch (error) { if (error.code === 'INSUFFICIENT_BALANCE') { console.error('Insufficient funds for payout'); } else if (error.code === 'INVALID_ACCOUNT') { console.error('Invalid beneficiary account details'); } else if (error.code === 'DUPLICATE_REFERENCE') { console.error('Reference already used, generate new one'); } else { console.error('Unexpected error:', error.message); } throw error; // Re-throw for upstream handling } ``` ## Best Practices * Use environment variables for API keys and configuration * Never commit sensitive credentials to version control * Use different API keys for sandbox and live environments * Always implement try-catch blocks for async operations * Log errors appropriately for debugging * Provide meaningful error messages to users * Use unique, timestamp-based references for payouts * Keep references under 30 characters * Include identifiable prefixes for easier tracking * Implement retry logic with exponential backoff * Monitor API usage to stay within limits * Cache frequently accessed data when appropriate ## Webhook Integration Handle webhook events for real-time notifications: ```javascript theme={null} const express = require('express'); const crypto = require('crypto'); const app = express(); app.use(express.json()); app.post('/webhook', (req, res) => { const signature = req.headers['carbon-signature']; const webhookSecret = process.env.CARBON_WEBHOOK_SECRET; // Verify webhook signature const computedSignature = crypto .createHmac('sha256', webhookSecret) .update(JSON.stringify(req.body)) .digest('hex'); if (signature === computedSignature) { const event = req.body; switch (event.type) { case 'account.incoming-transaction': console.log('Payment received:', event.data); break; case 'payout.successful': console.log('Payout completed:', event.data); break; default: console.log('Unknown event type:', event.type); } res.status(200).send('OK'); } else { res.status(401).send('Invalid signature'); } }); ``` ## Troubleshooting Ensure you're using the correct API key for your environment (sandbox vs live). Implement retry logic for network failures and timeout errors. Check that all required fields are provided and account numbers are valid before making API calls. ## Additional Resources * [Payment Operations Guide](/payment-operations) - Complete workflow examples * [Webhook Documentation](/webhooks/introduction) - Real-time event handling * [API Reference](/api-reference/introduction) - Detailed endpoint documentation * [Environment Setup](/environment) - Sandbox vs Live configuration For other programming languages or framework-specific integrations, please contact our support team at [sme@getcarbon.co](mailto:sme@getcarbon.co). # Model Context Protocol (MCP) Source: https://docs.getcarbon.co/mcp-documentation Enhance your AI development workflow with Carbon Business API integration through MCP for intelligent code assistance and documentation access. The Model Context Protocol (MCP) integration enables seamless access to Carbon Business API documentation and resources directly within AI-powered development tools like Claude Desktop, VS Code, and other MCP-compatible environments. ## What is MCP? Model Context Protocol is an open standard that allows AI assistants to securely access external data sources and tools. The Carbon Business API MCP server provides: * **Documentation Search**: Query API documentation, guides, and examples * **Code Examples**: Access ready-to-use code snippets for common operations * **API Reference**: Get detailed endpoint information and parameters * **Best Practices**: Retrieve recommended implementation patterns MCP enables AI assistants to provide more accurate and contextual assistance when working with Carbon Business API. ## Available Tools ### Tools Exposed to Connected AI Clients #### search\_carbon\_business\_api Search across the Carbon Business API knowledge base to find relevant information, code examples, API references, and guides. Use this tool when you need to answer questions about Carbon Business API, find specific documentation, understand how features work, or locate implementation details. The search returns contextual content with titles and direct links to the documentation pages. ## Installation ### Quick Start Install the Carbon Business API MCP server using our CLI tool: ```bash theme={null} npx @mintlify/mcp@latest add carbonmicrofinancebank ``` ### Hosted MCP server ```bash theme={null} https://docs.getcarbon.co/mcp ``` This command automatically configures the MCP server and adds it to your MCP client configuration. # Payment Operations Source: https://docs.getcarbon.co/payment-operations Learn how to create collection accounts and process payouts step-by-step using Carbon Business API. This guide covers essential payment operations including setting up collection accounts for receiving funds and processing payouts to beneficiaries. ## Creating Collection Accounts There are two types of accounts you can create via the API, each serving different purposes: Dashboard accounts are not accessible via the API. You must create accounts programmatically for API operations. ### Option 1: Collection Sub-Accounts (No Customer Required) Collection sub-accounts are designed for receiving payments and do not require customer creation. Use the [Create Account](/api-reference/accounts/create-account) endpoint directly for collection purposes: ```json theme={null} { "account_type": "static", "third_party": false, "account_name": "Collections Account" } ``` The API will return an account number that can receive payments from any bank in Nigeria. No customer creation is needed for this type of account. ### Option 2: Customer-Linked Business Accounts Business accounts are linked to specific customers and require customer creation first. Use the [Create Customer](/api-reference/customers/create-customer) endpoint to register customer details: ```json theme={null} { "email": "customer@example.com", "phone": "08012345678", "first_name": "John", "last_name": "Doe", "bvn": "12345678901" } ``` Use the [Create Account](/api-reference/accounts/create-account) endpoint with the customer ID: ```json theme={null} { "account_type": "static", "third_party": true, "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3" } ``` This creates a business account linked to the specific customer for more personalized banking operations. ### Monitoring Collections Track incoming payments using webhooks and transaction endpoints for both account types. Configure webhooks to receive real-time notifications for incoming transactions: * `account.incoming-transaction` - Triggered when funds are received See [Webhooks Documentation](/webhooks/introduction) for setup details. Use [Fetch Transactions](/api-reference/transactions/fetch-transactions) to retrieve payment history and verify collections. ## Processing Payouts Payouts allow you to send money from your API-created accounts to any Nigerian bank account. Minimum payout amount is ₦200. Source account must be created via the API. ### Step 1: Validate Beneficiary Account Before initiating any payout, validate the beneficiary's account details. Use the [Resolve Account](/api-reference/banks/resolve-account) endpoint to verify beneficiary information: ```json theme={null} { "account_number": "1234567890", "bank_code": "058" } ``` This endpoint returns the actual account name, which you'll need for the payout request. ### Step 2: Check Account Balance Ensure your source account has sufficient funds for the payout. Use [Fetch Balance](/api-reference/accounts/fetch-balance) to check available funds in your source account. Verify you have enough balance to cover both the payout amount and any applicable fees. ### Step 3: Create Payout Execute the payout using validated beneficiary details and your API-created source account. Use the [Create Payout](/api-reference/payouts/create-payout) endpoint: ```json theme={null} { "amount": 50000, "source": { "account_number": "your_api_account_number" }, "beneficiary": { "bank_code": "058", "bank_name": "GTBank", "account_number": "1234567890", "account_name": "Jane Smith" }, "reference": "PAYOUT_USER123_20260119", "remark": "Service payment" } ``` Use unique references for each payout to avoid duplicate transactions. Monitor payout progress using [Get Payout Status](/api-reference/payouts/get-payout-status) with your payout reference. ## Best Practices * Always use API-created accounts for programmatic operations * Keep track of customer-to-account relationships * Regularly monitor account balances before payouts * Always validate beneficiary accounts before payouts * Use unique references for each transaction * Implement webhook signature verification * Handle insufficient balance scenarios gracefully * Retry failed account validation requests * Log all transaction attempts for audit purposes ## Common Use Cases ### E-commerce Platform 1. Create collection accounts for merchants 2. Monitor incoming payments via webhooks 3. Process payouts to merchant bank accounts ### Payroll System 1. Create accounts for employee salary management 2. Fund accounts from company's main account 3. Distribute salaries using batch payouts ### Marketplace 1. Create collection accounts for sellers 2. Collect payments from buyers 3. Pay sellers after transaction completion For detailed API specifications and additional parameters, refer to the individual endpoint documentation in the API Reference section. # Quickstart Source: https://docs.getcarbon.co/quickstart Get started with the Carbon Business API by generating your API key, making your first request, and exploring the API. Before you can start using the Carbon Business API, you need to obtain your API credentials: 1. Log in to the [Carbon Business developer portal](https://carbon-business-client-staging.getcarbon.co/) 2. Navigate to the Developer menu (Top right dropdown > Developer) 3. Generate your `CARBON_API_KEY` or use an existing one 4. Contact our [integration team](mailto:sme@getcarbon.co) to obtain your `apikey` You'll need both keys for authentication: `apikey` (provided by our team) and `x-carbon-key` (generated from your dashboard). Test your integration with the health check endpoint to ensure everything is working correctly. ```bash cURL theme={null} curl -X GET https://carbonapistagingsecure.getcarbon.co/baas/api/health_check \ -H "apikey: YOUR_API_KEY" \ -H "x-carbon-key: YOUR_CARBON_API_KEY" ``` ```javascript JavaScript theme={null} const response = await fetch('https://carbonapistagingsecure.getcarbon.co/baas/api/health_check', { headers: { 'apikey': 'YOUR_API_KEY', 'x-carbon-key': 'YOUR_CARBON_API_KEY' } }); const data = await response.json(); console.log(data); ``` ```python Python theme={null} import requests url = 'https://carbonapistagingsecure.getcarbon.co/baas/api/health_check' headers = { 'apikey': 'YOUR_API_KEY', 'x-carbon-key': 'YOUR_CARBON_API_KEY' } response = requests.get(url, headers=headers) print(response.json()) ``` **Expected Response:** ```json theme={null} { "message": "OK" } ``` Before creating accounts, you need to create a customer record: ```javascript JavaScript theme={null} const customer = await fetch('https://carbonapistagingsecure.getcarbon.co/baas/api/v1/customers', { method: 'POST', headers: { 'apikey': 'YOUR_API_KEY', 'x-carbon-key': 'YOUR_CARBON_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ first_name: 'John', last_name: 'Doe', email: 'john.doe@example.com', phone: '08012345678', dob: '1990-01-01', gender: 'male', street: '123 Main St', city: 'Lagos', state: 'Lagos', country: 'Nigeria', bvn: '12345678901', nin: '12345678901' }) }); ``` ```python Python theme={null} customer_data = { 'first_name': 'John', 'last_name': 'Doe', 'email': 'john.doe@example.com', 'phone': '08012345678', 'dob': '1990-01-01', 'gender': 'male', 'street': '123 Main St', 'city': 'Lagos', 'state': 'Lagos', 'country': 'Nigeria', 'bvn': '12345678901', 'nin': '12345678901' } response = requests.post(url + '/v1/customers', headers=headers, json=customer_data) customer = response.json() ``` Now create a virtual account for your customer: ```javascript JavaScript theme={null} const account = await fetch('https://carbonapistagingsecure.getcarbon.co/baas/api/v1/accounts', { method: 'POST', headers: { 'apikey': 'YOUR_API_KEY', 'x-carbon-key': 'YOUR_CARBON_API_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ account_type: 'static', third_party: true, customer_id: 'customer-uuid-from-step-3' }) }); const accountData = await account.json(); console.log('Virtual account created:', accountData); ``` ```python Python theme={null} account_data = { 'account_type': 'static', 'third_party': True, 'customer_id': 'customer-uuid-from-step-3' } response = requests.post(url + '/v1/accounts', headers=headers, json=account_data) account = response.json() print('Virtual account created:', account) ``` ## What's Next? Configure webhook endpoints to receive real-time transaction notifications. Dive deeper into our comprehensive API documentation. Learn how to initiate payouts to beneficiary accounts. Understand how to verify and fetch transaction details. # Webhook Events Source: https://docs.getcarbon.co/webhooks/events Comprehensive guide to Carbon Business API webhook events and their payload structures. Carbon provides webhook events to notify your application about specific account activities in real-time. Below are the details of the supported webhook events and their payload structures. ## Available Events ### `account.incoming-transaction` This event is triggered when an incoming transaction is received in your account, such as when a customer makes a payment to your collection account. Use this event to automatically process incoming payments, update account balances, or trigger downstream business logic. #### Payload Structure ```json theme={null} { "event": "account.incoming-transaction", "data": { "id": "b44b9f33-179d-5d0g-0bbd-d4340387fcb2", "amount": 150000.00, "currency": "NGN", "transactionType": "CREDIT", "entryDate": "2026-01-19T10:30:45.048+0000", "uniqueRef": "209701944078087", "account": { "id": "5999182f-bccd-548f-bcee-c4350397fdc2", "bankAccount": { "accountName": "CARBON BUSINESS DEMO - JOHN DOE", "accountNumber": "0340899288", "bank": { "code": "565", "name": "CARBON" } }, "static": true, "currency": "NGN", "clientId": "528530847" }, "beneficiary": { "accountName": "CARBON BUSINESS DEMO - JOHN DOE", "accountNumber": "0340899288", "bankName": "CARBON" }, "sender": { "accountName": "JANE SMITH", "accountNumber": "2001234567", "bankName": "GTBank" }, "transactionDetails": { "remark": "TRF/TO/0340899288/Payment for services" } } } ``` #### Key Fields | Field | Type | Description | | -------------------------------- | -------- | --------------------------------------------------- | | `event` | string | Event type identifier | | `data.id` | string | Unique transaction identifier | | `data.amount` | float | Transaction amount in specified currency | | `data.currency` | string | Currency code (NGN) | | `data.transactionType` | string | Type of transaction (CREDIT for incoming) | | `data.entryDate` | datetime | ISO 8601 timestamp of transaction | | `data.uniqueRef` | string | Unique reference for the transaction | | `data.account` | object | Account details where transaction occurred | | `data.beneficiary` | object | Recipient account information (your Carbon account) | | `data.beneficiary.accountName` | string | Name of the receiving account holder | | `data.beneficiary.accountNumber` | string | Receiving account number | | `data.beneficiary.bankName` | string | Name of the receiving bank | | `data.sender` | object | Sender account information (external sender) | | `data.sender.accountName` | string | Name of the sender account holder | | `data.sender.accountNumber` | string | Sender's account number | | `data.sender.bankName` | string | Name of the sender's bank | | `data.transactionDetails` | object | Additional transaction information | | `data.transactionDetails.remark` | string | Transaction description or note | *** ### `account.outgoing-transaction` This event is triggered when an outgoing transaction is made from your account, such as when you process a payout to a beneficiary. Use this event to track payout completions, update transaction records, or notify users about successful transfers. #### Payload Structure ```json theme={null} { "event": "account.outgoing-transaction", "data": { "id": "a33a8f22-068c-4c9f-9aac-c98edaf7a698", "amount": 10157.5, "currency": "NGN", "transactionType": "DEBIT", "entryDate": "2026-01-19T12:44:44.048+0000", "uniqueRef": "208690833967976", "account": { "id": "4888071e-abbe-437e-abdf-b3239286ebc1", "bankAccount": { "accountName": "CARBON BUSINESS DEMO - MARK ERELU", "accountNumber": "0340899287", "bank": { "code": "565", "name": "CARBON" } }, "static": true, "currency": "NGN", "clientId": "528530846" }, "beneficiary": { "accountName": "DOE JOHN", "accountNumber": "9002118329", "bankName": "Paycom (Opay)" }, "sender": { "accountName": "CARBON BUSINESS DEMO - MARK ERELU", "accountNumber": "0340899287", "bankName": "CARBON" }, "transactionDetails": { "remark": "TRF/FRM/9002118329/TEST - 100" } } } ``` #### Key Fields | Field | Type | Description | | -------------------------------- | -------- | --------------------------------------------- | | `event` | string | Event type identifier | | `data.id` | string | Unique transaction identifier | | `data.amount` | float | Transaction amount in specified currency | | `data.currency` | string | Currency code (NGN) | | `data.transactionType` | string | Type of transaction (DEBIT for outgoing) | | `data.entryDate` | datetime | ISO 8601 timestamp of transaction | | `data.uniqueRef` | string | Reference used in the original payout request | | `data.account` | object | Source account details for the transaction | | `data.beneficiary` | object | Recipient account information | | `data.beneficiary.accountName` | string | Name of the beneficiary account holder | | `data.beneficiary.accountNumber` | string | Beneficiary's account number | | `data.beneficiary.bankName` | string | Name of the beneficiary's bank | | `data.sender` | object | Sender account information | | `data.sender.accountName` | string | Name of the sender account holder | | `data.sender.accountNumber` | string | Sender's account number | | `data.sender.bankName` | string | Name of the sender's bank | | `data.transactionDetails` | object | Additional transaction information | | `data.transactionDetails.remark` | string | Transaction description or note | *** ## Loan Events Carbon fires the following events throughout the loan lifecycle. Subscribe to these to track application progress and drive your customer-facing UI. ### `loan.application.received` Fired when a loan application is successfully submitted. #### Payload Structure ```json theme={null} { "event": "loan.application.received", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "amount": 2000000, "repayment_period": 3, "status": "PENDING", "created_at": "2024-05-15T10:00:00Z" } } ``` *** ### `loan.kyc.pending` Fired when KYC verification is initiated for a loan application. #### Payload Structure ```json theme={null} { "event": "loan.kyc.pending", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "kyc_status": "PENDING" } } ``` *** ### `loan.offer.generated` Fired when underwriting completes and an offer is ready for the customer. #### Payload Structure ```json theme={null} { "event": "loan.offer.generated", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "offered_amount": 2000000, "tenure": 3, "interest_rate": 8, "monthly_repayment": 719999, "expires_at": "2024-06-15T10:00:00Z" } } ``` *** ### `loan.offer.accepted` Fired when the customer accepts the loan offer. #### Payload Structure ```json theme={null} { "event": "loan.offer.accepted", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "status": "ACCEPTED_OFFER" } } ``` *** ### `loan.offer.declined` Fired when the customer declines the loan offer. #### Payload Structure ```json theme={null} { "event": "loan.offer.declined", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "decline_reason": "HIGH_INTEREST" } } ``` *** ### `loan.offer.expired` Fired when a loan offer expires without the customer taking action. #### Payload Structure ```json theme={null} { "event": "loan.offer.expired", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "expired_at": "2024-06-15T10:00:00Z" } } ``` *** ### `loan.application.declined` Fired when the application is rejected by the credit decisioning engine. #### Payload Structure ```json theme={null} { "event": "loan.application.declined", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "decline_reasons": ["INSUFFICIENT_REVENUE", "HIGH_EXISTING_DEBT"] } } ``` *** ### `loan.application.approved` Fired when the loan is approved for disbursement by a Carbon admin. #### Payload Structure ```json theme={null} { "event": "loan.application.approved", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "approved_amount": 2000000 } } ``` *** ### `loan.disbursed` Fired when loan funds are disbursed to the customer's account. The `loan_id` field becomes available after this event — use it for repayment routes. #### Payload Structure ```json theme={null} { "event": "loan.disbursed", "data": { "application_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "loan_id": "LOAN_ABC123", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "disbursed_amount": 2000000, "disbursement_date": "2024-05-20T08:00:00Z" } } ``` *** ### `loan.arrears` Fired when a loan becomes overdue. #### Payload Structure ```json theme={null} { "event": "loan.arrears", "data": { "loan_id": "LOAN_ABC123", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "outstanding_balance": 150000000, "days_overdue": 7 } } ``` *** ### `loan.closed` Fired when the loan is fully repaid. #### Payload Structure ```json theme={null} { "event": "loan.closed", "data": { "loan_id": "LOAN_ABC123", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "closed_at": "2024-08-20T12:00:00Z" } } ``` *** ### `loan.repayment.successful` Fired when a repayment charge succeeds. #### Payload Structure ```json theme={null} { "event": "loan.repayment.successful", "data": { "loan_id": "LOAN_ABC123", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "amount": 7199999, "reference": "REPAY_REF_20250215_001", "outstanding_balance": 142800001, "repayment_date": "2025-02-15T09:00:00Z" } } ``` *** ### `loan.repayment.failed` Fired when a repayment charge attempt fails. #### Payload Structure ```json theme={null} { "event": "loan.repayment.failed", "data": { "loan_id": "LOAN_ABC123", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "amount": 7199999, "reference": "REPAY_REF_20250215_001", "failure_reason": "INSUFFICIENT_FUNDS" } } ``` *** ### `loan.repayment.due` Fired 3 days before a repayment due date as a reminder. #### Payload Structure ```json theme={null} { "event": "loan.repayment.due", "data": { "loan_id": "LOAN_ABC123", "customer_id": "1732ca47-42b2-4990-a65d-c369e934eed3", "amount_due": 7199999, "due_date": "2025-02-15" } } ``` *** ## Implementation Notes Always respond with a 2xx status code to acknowledge receipt. Carbon will retry delivery if no acknowledgment is received. Use the `data.id` field to handle duplicate events gracefully. The same transaction may trigger multiple webhook deliveries. Always verify webhook signatures before processing events to ensure they originate from Carbon. ## Next Steps For more information on setting up and managing webhooks, refer to the [Webhooks Introduction](/webhooks/introduction) documentation. # Introduction Source: https://docs.getcarbon.co/webhooks/introduction # Webhooks Introduction Webhooks enable your application to receive instant notifications about important events, making it easy to automate workflows and keep external systems in sync with Carbon's API. ## What Are Webhooks? Webhooks are HTTP callbacks triggered by specific events in your Carbon account. When an event occurs, Carbon sends a POST request to your configured webhook URL, allowing your server to process the event in real time. ## How Webhooks Work Set your webhook endpoint using the Carbon Business dashboard. Make sure your server is ready to accept POST requests. When a relevant event occurs (e.g., a transaction is completed), Carbon automatically sends a POST request to your webhook URL with event details. Your server should respond with a 2xx status code to confirm successful receipt. If not, Carbon may retry delivery. ## Key Features Receive immediate updates for events such as transactions, account changes, and more. Easily update your webhook URL or select which events you want to subscribe to. Each webhook request includes a HMAC-SHA256 signature in the `X-Carbon-Baas-Signature` header for verification. The signature is calculated by JSON-encoding the payload and hashing it with your secret key. Always validate the signature to ensure authenticity and prevent tampering. ## Example Webhook Events Some common events you can subscribe to: **Account events:** * `account.incoming-transaction` * `account.outgoing-transaction` **Loan events:** * `loan.application.received` * `loan.offer.generated` * `loan.offer.accepted` * `loan.disbursed` * `loan.repayment.successful` * `loan.closed` For a full list and details, see the [Webhook Events](/webhooks/events) documentation. ## Best Practices Always verify the signature included in webhook requests to confirm they originate from Carbon. ### How Signature Verification Works Carbon signs each webhook with HMAC-SHA256 using your webhook secret and sends the signature in the `X-Carbon-Baas-Signature` header. **Signature Generation:** 1. JSON-encode the webhook payload 2. Create HMAC-SHA256 hash using your secret key 3. The result is sent in the `X-Carbon-Baas-Signature` header ### Finding Your Webhook Secret Your webhook secret can be found in your Carbon dashboard on the **Developer** page. ### Implementation Examples ```php PHP theme={null} { const signature = req.headers['x-carbon-baas-signature']; const secret = process.env.CARBON_WEBHOOK_SECRET; if (!verifyWebhookSignature(req.body, signature, secret)) { return res.status(401).send('Invalid signature'); } const data = JSON.parse(req.body); // Process webhook data... res.status(200).send('OK'); }); ``` ```python Python theme={null} import hmac import hashlib import json def verify_webhook_signature(payload, signature, secret): expected_signature = hmac.new( secret.encode('utf-8'), payload.encode('utf-8'), hashlib.sha256 ).hexdigest() return hmac.compare_digest(expected_signature, signature) # Flask example from flask import Flask, request @app.route('/webhook', methods=['POST']) def webhook(): payload = request.get_data(as_text=True) signature = request.headers.get('X-Carbon-Baas-Signature') secret = os.environ.get('CARBON_WEBHOOK_SECRET') if not verify_webhook_signature(payload, signature, secret): return 'Invalid signature', 401 data = json.loads(payload) # Process webhook data... return 'OK', 200 ``` ```ruby Ruby theme={null} require 'openssl' require 'json' def verify_webhook_signature(payload, signature, secret) expected_signature = OpenSSL::HMAC.hexdigest('sha256', secret, payload) Rack::Utils.secure_compare(expected_signature, signature) end # Sinatra example post '/webhook' do payload = request.body.read signature = request.env['HTTP_X_CARBON_BAAS_SIGNATURE'] secret = ENV['CARBON_WEBHOOK_SECRET'] unless verify_webhook_signature(payload, signature, secret) halt 401, 'Invalid signature' end data = JSON.parse(payload) # Process webhook data... status 200 end ``` ### Security Best Practices * **Use constant-time comparison**: Always use `hash_equals()` (PHP), `crypto.timingSafeEqual()` (Node.js), `hmac.compare_digest()` (Python), or equivalent functions to prevent timing attacks * **Validate before processing**: Never process webhook data before verifying the signature * **Keep secrets secure**: Store your webhook secret as an environment variable, never in your codebase * **Use HTTPS**: Always use HTTPS endpoints to protect data in transit If your server does not respond with a 2xx status code, Carbon will retry delivery up to 3 times. Ensure your endpoint can handle duplicate events safely. ### Manual Resend Options If automatic retries fail, you can manually resend webhook events: * **Dashboard**: Navigate to the **Nexus** menu in your Carbon dashboard to resend failed webhooks * **API**: Use the [Resend Webhook Event](/api-reference/webhooks/resend-webhook-event) API endpoint for programmatic resending This gives you full control over webhook delivery and allows you to retry specific events as needed. Set up logging and monitoring for your webhook endpoint to track received events and troubleshoot issues quickly. ## Troubleshooting & Support If you experience issues with webhook delivery or event processing, check your server logs and ensure your endpoint is publicly accessible. For further assistance, refer to the [Error Handling](/error) documentation or contact Carbon support.