> ## 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.

# Create and manage Dubu Pay API keys

> Generate long-lived API keys for server-side integrations, list active keys, revoke compromised keys, and follow security best practices.

API keys are the recommended authentication method for server-side integrations. Unlike JWT tokens, they do not expire automatically, which makes them well-suited for background services, cron jobs, and backend applications that make API calls without an interactive login flow. All API key endpoints require an authenticated request — use either a bearer token or an existing API key.

<Warning>
  The full API key secret is shown **only once**, immediately after creation. If you lose it, you must delete the key and create a new one. Store keys in a secret manager or environment variable as soon as you receive them.
</Warning>

## Key format

Dubu Pay API keys follow a predictable format so they are easy to identify and can be detected by secret-scanning tools:

| Environment | Format                                |
| ----------- | ------------------------------------- |
| Live        | `dubu_sk_live_<32 random characters>` |
| Sandbox     | `dubu_sk_test_<32 random characters>` |

After creation, only the first 20 characters (`key_prefix`) are stored and returned by list endpoints. Use the prefix to identify which key you are viewing without exposing the secret.

***

## Create an API key

`POST /api-keys`

Creates a new API key scoped to your merchant account. The response includes the full plaintext `key` field — this is the only time it is returned.

**Request body**

<ParamField body="name" type="string" required>
  A human-readable label for this key. Use something descriptive like `"production-backend"` or `"staging-worker"`. Maximum 100 characters.
</ParamField>

<ParamField body="environment" type="string">
  `"sandbox"` or `"live"`. Defaults to `"sandbox"`. Sandbox keys carry the prefix `dubu_sk_test_`; live keys carry `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-backend",
      "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-backend',
      environment: 'live',
    }),
  });
  const { data } = await response.json();
  // data.key is the full secret — store it now
  console.log(data.key);
  ```
</CodeGroup>

**Example response**

```json theme={null}
{
  "success": true,
  "data": {
    "id": "3f7a1c2e-84d0-4b1a-9c6f-2e5d8a0b7f3c",
    "name": "production-backend",
    "key_prefix": "dubu_sk_live_AbCdEf",
    "environment": "live",
    "created_at": "2025-09-01T10:05:00.000Z",
    "key": "dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345"
  }
}
```

**Response fields**

<ResponseField name="id" type="string" required>
  UUID of the API key. Use this in revoke and delete requests.
</ResponseField>

<ResponseField name="name" type="string" required>
  The label you assigned to this key.
</ResponseField>

<ResponseField name="key_prefix" type="string" required>
  First 20 characters of the key. Safe to display in logs and list views.
</ResponseField>

<ResponseField name="environment" type="string" required>
  `"sandbox"` or `"live"`.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 timestamp of when the key was created.
</ResponseField>

<ResponseField name="key" type="string" required>
  The full plaintext API key. **Returned only in this response.** Store it immediately.
</ResponseField>

***

## List API keys

`GET /api-keys`

Returns all API keys associated with your merchant account. The `key` secret is never included in list responses — only the `key_prefix` is returned for identification.

<CodeGroup>
  ```bash cURL theme={null}
  curl --request GET \
    --url https://api.dubupay.com/api/v1/api-keys \
    --header 'X-Api-Key: dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.dubupay.com/api/v1/api-keys', {
    headers: {
      'X-Api-Key': process.env.DUBU_API_KEY,
    },
  });
  const { data } = await response.json();
  ```
</CodeGroup>

**Example response**

```json theme={null}
{
  "success": true,
  "data": [
    {
      "id": "3f7a1c2e-84d0-4b1a-9c6f-2e5d8a0b7f3c",
      "name": "production-backend",
      "key_prefix": "dubu_sk_live_AbCdEf",
      "environment": "live",
      "is_active": true,
      "last_used_at": "2025-09-01T14:22:00.000Z",
      "created_at": "2025-09-01T10:05:00.000Z"
    },
    {
      "id": "a1b2c3d4-...",
      "name": "staging-worker",
      "key_prefix": "dubu_sk_test_XyZaBc",
      "environment": "sandbox",
      "is_active": false,
      "last_used_at": null,
      "created_at": "2025-08-15T08:00:00.000Z"
    }
  ]
}
```

**Response fields (per key)**

<ResponseField name="id" type="string" required>
  UUID of the API key.
</ResponseField>

<ResponseField name="name" type="string" required>
  Human-readable label.
</ResponseField>

<ResponseField name="key_prefix" type="string" required>
  First 20 characters of the key for identification.
</ResponseField>

<ResponseField name="environment" type="string" required>
  `"sandbox"` or `"live"`.
</ResponseField>

<ResponseField name="is_active" type="boolean" required>
  `true` if the key can be used to authenticate requests. `false` if the key has been revoked.
</ResponseField>

<ResponseField name="last_used_at" type="string">
  ISO 8601 timestamp of the most recent authenticated request using this key. `null` if the key has never been used.
</ResponseField>

<ResponseField name="created_at" type="string" required>
  ISO 8601 timestamp of when the key was created.
</ResponseField>

***

## Revoke an API key

`PATCH /api-keys/:id/revoke`

Revokes the key, setting `is_active` to `false`. Revoked keys are rejected on all subsequent requests but remain visible in the key list. Use revoke when you want to disable a key temporarily or audit it before permanent deletion.

<ParamField path="id" type="string" required>
  UUID of the API key to revoke.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request PATCH \
    --url https://api.dubupay.com/api/v1/api-keys/3f7a1c2e-84d0-4b1a-9c6f-2e5d8a0b7f3c/revoke \
    --header 'X-Api-Key: dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345'
  ```

  ```javascript Node.js theme={null}
  const keyId = '3f7a1c2e-84d0-4b1a-9c6f-2e5d8a0b7f3c';
  const response = await fetch(
    `https://api.dubupay.com/api/v1/api-keys/${keyId}/revoke`,
    {
      method: 'PATCH',
      headers: { 'X-Api-Key': process.env.DUBU_API_KEY },
    }
  );
  const result = await response.json();
  ```
</CodeGroup>

**Example response**

```json theme={null}
{
  "success": true,
  "message": "API key revoked"
}
```

***

## Delete an API key

`DELETE /api-keys/:id`

Permanently removes the API key from your account. This action cannot be undone. Use deletion when you are certain you no longer need the key record.

<ParamField path="id" type="string" required>
  UUID of the API key to delete.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl --request DELETE \
    --url https://api.dubupay.com/api/v1/api-keys/3f7a1c2e-84d0-4b1a-9c6f-2e5d8a0b7f3c \
    --header 'X-Api-Key: dubu_sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz012345'
  ```

  ```javascript Node.js theme={null}
  const keyId = '3f7a1c2e-84d0-4b1a-9c6f-2e5d8a0b7f3c';
  const response = await fetch(
    `https://api.dubupay.com/api/v1/api-keys/${keyId}`,
    {
      method: 'DELETE',
      headers: { 'X-Api-Key': process.env.DUBU_API_KEY },
    }
  );
  const result = await response.json();
  ```
</CodeGroup>

**Example response**

```json theme={null}
{
  "success": true,
  "message": "API key deleted"
}
```

***

## Security best practices

<AccordionGroup>
  <Accordion title="Store keys in environment variables or a secret manager">
    Never hard-code an API key in your source code. Use environment variables in local development (e.g. `DUBU_API_KEY=dubu_sk_live_...`) and a secrets manager such as AWS Secrets Manager, HashiCorp Vault, or your platform's built-in secrets store in production.
  </Accordion>

  <Accordion title="Never expose keys in client-side code">
    API keys must only be used in server-side code. If a key appears in a browser, a mobile app binary, or a public repository, treat it as compromised and rotate it immediately.
  </Accordion>

  <Accordion title="Rotate keys regularly">
    Create a replacement key before revoking the existing one to avoid downtime. Aim to rotate live keys at least every 90 days, or immediately after any suspected exposure.
  </Accordion>

  <Accordion title="Use separate keys per environment and service">
    Create one key per environment (sandbox, live) and ideally one per service or deployment. This limits the blast radius of a compromised key and makes it easy to rotate without affecting other services.
  </Accordion>

  <Accordion title="Monitor last_used_at">
    Check the `last_used_at` field when listing keys. Keys that have never been used or that have been idle for an extended period are candidates for deletion.
  </Accordion>
</AccordionGroup>

<Tip>
  Use a sandbox key (`dubu_sk_test_...`) during development and testing. Switch to a live key only in production deployments.
</Tip>

***

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication overview" icon="shield" href="/authentication/overview">
    Compare JWT bearer tokens and API keys, and learn how to refresh tokens.
  </Card>

  <Card title="Quickstart" icon="rocket" href="/quickstart">
    Walk through registering, creating a key, and issuing a virtual bank account.
  </Card>
</CardGroup>
