> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dubupay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Get started with Dubu Pay in minutes

> Register a merchant account, get your API key, create a customer, and issue a virtual bank account to start accepting NGN payments.

Dubu Pay gives you a fully managed payment and trading experience through a single REST API. By the end of this guide you will have a working merchant account, a live API key, a customer record, and a virtual bank account ready to receive NGN payments.

<Note>
  All API requests use the base URL `https://api.dubupay.com/api/v1`. Responses follow the shape `{ "success": true, "data": { ... } }` for successful requests.
</Note>

## Prerequisites

* A valid business email address
* Node.js 18+ or any HTTP client (the examples below use cURL and the native `fetch` API)

***

## Steps

<Steps>
  <Step title="Register your merchant account">
    Send a `POST` request to `/auth/register` with your business name, email, and a password of at least eight characters.

    **Request body**

    <ParamField body="business_name" type="string" required>
      Your legal business or trading name. Minimum two characters.
    </ParamField>

    <ParamField body="email" type="string" required>
      The email address you will use to sign in to Dubu Pay.
    </ParamField>

    <ParamField body="password" type="string" required>
      A password of at least eight characters.
    </ParamField>

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url https://api.dubupay.com/api/v1/auth/register \
        --header 'Content-Type: application/json' \
        --data '{
          "business_name": "Acme Store",
          "email": "dev@acme.io",
          "password": "supersecret123"
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch('https://api.dubupay.com/api/v1/auth/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          business_name: 'Acme Store',
          email: 'dev@acme.io',
          password: 'supersecret123',
        }),
      });
      const { data } = await response.json();
      ```
    </CodeGroup>

    **Example response**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "id": "mch_01j9k2p...",
        "business_name": "Acme Store",
        "email": "dev@acme.io",
        "is_verified": false,
        "created_at": "2025-09-01T10:00:00.000Z"
      }
    }
    ```
  </Step>

  <Step title="Verify your email address">
    After registering, Dubu Pay sends a six-digit OTP to your email. Submit it to `/auth/verify-email` to activate your account.

    <ParamField body="email" type="string" required>
      The email address you registered with.
    </ParamField>

    <ParamField body="otp" type="string" required>
      The six-digit numeric code sent to your inbox.
    </ParamField>

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url https://api.dubupay.com/api/v1/auth/verify-email \
        --header 'Content-Type: application/json' \
        --data '{
          "email": "dev@acme.io",
          "otp": "482910"
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch('https://api.dubupay.com/api/v1/auth/verify-email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: 'dev@acme.io', otp: '482910' }),
      });
      const { data } = await response.json();
      ```
    </CodeGroup>

    **Example response**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "access_token": "eyJhbGci...",
        "refresh_token": "eyJhbGci..."
      }
    }
    ```

    <Tip>
      If your OTP expires before you can use it, call `POST /auth/resend-otp` with your email to request a new one.
    </Tip>
  </Step>

  <Step title="Log in and obtain tokens">
    Log in to receive an access token (valid for 15 minutes) and a refresh token (valid for 7 days). You need the access token to complete onboarding steps in the dashboard and to create your first API key.

    <ParamField body="email" type="string" required>
      Your registered email address.
    </ParamField>

    <ParamField body="password" type="string" required>
      Your account password.
    </ParamField>

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url https://api.dubupay.com/api/v1/auth/login \
        --header 'Content-Type: application/json' \
        --data '{
          "email": "dev@acme.io",
          "password": "supersecret123"
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch('https://api.dubupay.com/api/v1/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ email: 'dev@acme.io', password: 'supersecret123' }),
      });
      const { data } = await response.json();
      const { access_token, refresh_token } = data;
      ```
    </CodeGroup>

    **Example response**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "access_token": "eyJhbGci...",
        "refresh_token": "eyJhbGci...",
        "merchant": {
          "id": "mch_01j9k2p...",
          "business_name": "Acme Store",
          "email": "dev@acme.io"
        }
      }
    }
    ```
  </Step>

  <Step title="Create an API key">
    For server-side integrations, API keys are more convenient than short-lived JWT tokens. Create one now using the access token from the previous step.

    <Warning>
      The full API key is returned **only once** at creation time. Copy it immediately and store it in a secret manager or environment variable. You cannot retrieve it again.
    </Warning>

    <ParamField body="name" type="string" required>
      A human-readable label for this key, such as `"production-server"` or `"staging"`. Maximum 100 characters.
    </ParamField>

    <ParamField body="environment" type="string">
      `"sandbox"` (default) or `"live"`. Sandbox keys have the prefix `dubu_sk_test_`; live keys have `dubu_sk_live_`.
    </ParamField>

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url https://api.dubupay.com/api/v1/api-keys \
        --header 'Authorization: Bearer <access_token>' \
        --header 'Content-Type: application/json' \
        --data '{
          "name": "production-server",
          "environment": "live"
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch('https://api.dubupay.com/api/v1/api-keys', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${access_token}`,
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ name: 'production-server', environment: 'live' }),
      });
      const { data } = await response.json();
      const apiKey = data.key; // store this immediately
      ```
    </CodeGroup>

    **Example response**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "id": "3f7a1c2e-...",
        "name": "production-server",
        "key_prefix": "dubu_sk_live_AbCdEf",
        "environment": "live",
        "created_at": "2025-09-01T10:05:00.000Z",
        "key": "dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345"
      }
    }
    ```

    From this point on, pass the API key in the `X-Api-Key` header instead of a `Bearer` token.
  </Step>

  <Step title="Create your first customer">
    Before issuing a virtual bank account, you need a customer record. Customers represent the end-users who will send payments to you.

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url https://api.dubupay.com/api/v1/payments/customers \
        --header 'X-Api-Key: dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345' \
        --header 'Content-Type: application/json' \
        --data '{
          "first_name": "Amara",
          "last_name": "Okafor",
          "email": "amara@example.com",
          "phone": "+2348012345678"
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch('https://api.dubupay.com/api/v1/payments/customers', {
        method: 'POST',
        headers: {
          'X-Api-Key': 'dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          first_name: 'Amara',
          last_name: 'Okafor',
          email: 'amara@example.com',
          phone: '+2348012345678',
        }),
      });
      const { data } = await response.json();
      const customerId = data.id;
      ```
    </CodeGroup>

    **Example response**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "id": "cus_9f3b...",
        "first_name": "Amara",
        "last_name": "Okafor",
        "email": "amara@example.com",
        "phone": "+2348012345678",
        "created_at": "2025-09-01T10:06:00.000Z"
      }
    }
    ```
  </Step>

  <Step title="Issue a virtual bank account">
    With a customer ID in hand, create a virtual bank account (onramp) so your customer can send an NGN payment. Use `TEMPORARY` type for a one-time payment with a locked exchange rate and a 25-minute expiry, or `PERMANENT` type for a reusable account (requires customer BVN).

    <CodeGroup>
      ```bash cURL theme={null}
      curl --request POST \
        --url https://api.dubupay.com/api/v1/payments/onramps \
        --header 'X-Api-Key: dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345' \
        --header 'Content-Type: application/json' \
        --data '{
          "customer_id": "cus_9f3b...",
          "type": "TEMPORARY",
          "amount": 50000
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch('https://api.dubupay.com/api/v1/payments/onramps', {
        method: 'POST',
        headers: {
          'X-Api-Key': 'dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          customer_id: 'cus_9f3b...',
          type: 'TEMPORARY',
          amount: 50000,
        }),
      });
      const { data } = await response.json();
      ```
    </CodeGroup>

    **Example response**

    ```json theme={null}
    {
      "success": true,
      "data": {
        "id": "onr_7d2a...",
        "type": "TEMPORARY",
        "status": "ACTIVE",
        "amount": 50000,
        "currency": "NGN",
        "account_number": "0123456789",
        "bank_name": "Wema Bank",
        "account_name": "DUBUPAY / ACME STORE",
        "expires_at": "2025-09-01T10:31:00.000Z",
        "created_at": "2025-09-01T10:06:00.000Z"
      }
    }
    ```

    Share the `account_number` and `bank_name` with your customer. When they transfer funds, a deposit record is created and progresses through the statuses `PENDING_FX` → `PENDING_WITHDRAWAL` → `SETTLED`.
  </Step>
</Steps>

***

## What's next

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/authentication/overview">
    Understand JWT tokens, API keys, and when to use each.
  </Card>

  <Card title="API keys" icon="terminal" href="/authentication/api-keys">
    Create, list, revoke, and delete API keys for your integration.
  </Card>
</CardGroup>
