# Validate a bill customer (/docs/api-reference/bills/validate-a-bill-customer)

Validates a meter, smartcard, internet account, or phone number with the selected provider before payment.

OpenAPI operation (JSON):
{
  "openapi": "3.2.0",
  "paths": {
    "/api/v1/payments/collections": {
      "post": {
        "description": "Requires collections:create. Supports bank transfer, USSD, and QR without accepting raw card data. Use POST /v1/checkout/sessions for card-capable hosted checkout. Atomically records a pending transaction before provider initialization. Retry with the same key and body to replay the initial response.",
        "operationId": "initialize-a-server-side-collection",
        "parameters": [
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-idempotency-key",
            "in": "header",
            "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCollectionDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionCreationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input, missing wallet, or missing payer email/phone required by Paga."
          },
          "401": {
            "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist."
          },
          "403": {
            "description": "API key lacks the required permission or environment is not allowed."
          },
          "409": {
            "description": "Idempotency key already used for a different request."
          },
          "503": {
            "description": "Paga credentials or callback URL are missing for the selected environment. No funds or records are changed."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Initialize a server-side collection",
        "tags": [
          "Collections"
        ]
      }
    },
    "/api/v1/payouts/recipients/validate": {
      "post": {
        "description": "Requires payouts:create. Uses the environment on the API key and Paga Business Validate Deposit to Bank. Simulated sandbox behavior is available only when PAGA_SANDBOX_SIMULATE=true.",
        "operationId": "validate-a-payout-recipient",
        "parameters": [
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-idempotency-key",
            "in": "header",
            "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateRecipientDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RecipientValidationResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist."
          },
          "403": {
            "description": "API key lacks the required permission or environment is not allowed."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Validate a payout recipient",
        "tags": [
          "Payouts"
        ]
      }
    },
    "/api/v1/payouts": {
      "post": {
        "description": "Requires payouts:create. Validates the recipient, atomically reserves funds, and writes a durable payout-dispatch record before BullMQ sends it to Paga. Provider calls are never retried automatically after dispatch begins.",
        "operationId": "queue-a-payout",
        "parameters": [
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-idempotency-key",
            "in": "header",
            "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePayoutDto"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PayoutCreationResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input, missing wallet, or insufficient balance."
          },
          "401": {
            "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist."
          },
          "403": {
            "description": "API key lacks the required permission or environment is not allowed."
          },
          "409": {
            "description": "Idempotency key already used for a different request."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Queue a payout",
        "tags": [
          "Payouts"
        ]
      }
    },
    "/api/v1/virtual-accounts": {
      "get": {
        "description": "Requires virtual-accounts:read. Results are scoped to the API key environment. Provision accounts through the Business owner/admin API.",
        "operationId": "list-dedicated-collection-accounts",
        "parameters": [
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/VirtualAccountResponse"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist."
          },
          "403": {
            "description": "API key lacks the required permission or environment is not allowed."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List dedicated collection accounts",
        "tags": [
          "Virtual accounts"
        ]
      }
    },
    "/api/v1/sandbox/virtual-accounts/{id}/transfers": {
      "post": {
        "description": "Requires sandbox:simulate and a sandbox API key. Immediately creates and posts a collection ledger entry and credits available balance. This route simulates a verified bank notification; it does not accept or process real transfers.",
        "operationId": "simulate-an-incoming-transfer-to-a-sandbox-virtual-account",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "description": "Sandbox virtual-account ID returned by the list/provision endpoints.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-idempotency-key",
            "in": "header",
            "description": "1–128 letters/digits or ._:-. Same key and payload replay for 24 hours; conflicting, in-flight, or expired keys return 409. Never change a key to retry an uncertain payment.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimulateVirtualTransferDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "transactionId",
                    "reference",
                    "status",
                    "amountMinor",
                    "currency",
                    "virtualAccountId"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "transactionId": {
                      "type": "string"
                    },
                    "reference": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "SUCCESS"
                      ]
                    },
                    "amountMinor": {
                      "type": "string"
                    },
                    "currency": {
                      "type": "string"
                    },
                    "provider": {
                      "type": "string"
                    },
                    "providerReference": {
                      "type": "string"
                    },
                    "virtualAccountId": {
                      "type": "string"
                    },
                    "requiresReconciliation": {
                      "type": "boolean",
                      "examples": [
                        false
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist."
          },
          "403": {
            "description": "API key lacks the required permission or environment is not allowed."
          },
          "404": {
            "description": "No active virtual account belonging to this sandbox merchant."
          },
          "409": {
            "description": "Duplicate/changed idempotency key or duplicate collection reference."
          },
          "501": {
            "description": "Live virtual account callbacks are not implemented."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "Simulate an incoming transfer to a sandbox virtual account",
        "tags": [
          "Virtual accounts"
        ]
      }
    },
    "/api/v1/bills/categories": {
      "get": {
        "description": "Uses the environment attached to the API key. VTpass is selected by default; pass FLUTTERWAVE to use the secondary provider.",
        "operationId": "list-bill-categories",
        "parameters": [
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "provider",
            "required": false,
            "in": "query",
            "schema": {
              "default": "VTPASS",
              "type": "string",
              "enum": [
                "VTPASS",
                "FLUTTERWAVE"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillCatalogResponse"
                }
              }
            }
          },
          "401": {
            "description": "Invalid API key/secret, signed request, timestamp, or IP whitelist."
          },
          "403": {
            "description": "API key lacks the required permission or environment is not allowed."
          }
        },
        "security": [
          {
            "api-key": []
          }
        ],
        "summary": "List bill categories",
        "tags": [
          "Bills"
        ]
      }
    },
    "/api/v1/bills/billers": {
      "get": {
        "operationId": "list-billers-services-for-a-provider-category",
        "parameters": [
          {
            "name": "x-api-secret",
            "in": "header",
            "description": "Recommended: API secret sent over HTTPS. You may send it as Authorization: Bearer instead. If supplied, x-timestamp and x-signature are not required.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-timestamp",
            "in": "header",
            "description": "Legacy signed mode: Unix time in milliseconds; maximum clock skew five minutes.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "x-signature",
            "in": "header",
            "description": "Legacy signed mode: HMAC-SHA256(secret, timestamp.METHOD.url.idempotencyKey.bodyHash).",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "provider",
            "required": false,
            "in": "query",
            "schema": {
              "default": "VTPASS",
              "type": "string",
              "enum": [
                "VTPASS",
                "FLUTTERWAVE"
              ]
            }
          },
          {
            "name": "category",
            "re