# Dbrij Ping Documentation

Everything you need to send SMS with Dbrij Ping: sending, message kinds and routes, delivery statuses, sender names, webhooks and how charging works.

Canonical page: https://dbrij.com/products/ping/docs

## Dbrij Ping

Ping sends text messages to every Nigerian network from your own code or from the dashboard: one time codes, alerts, reminders and campaigns. It is billed in units you buy in advance, and once your business is verified it works under a shared Dbrij sender name while your own name is being approved.

Three things are worth knowing before you build.

- **You are charged when a message is sent, not when it is delivered.** The networks charge for every message handed to them, whatever happens next, so Ping does too. A message that never left Dbrij is not charged. Keep your lists clean: a dead number costs the same as a live one.
- **You say what a message is, and Ping chooses the route.** A one time code always takes the route that reaches phones with Do Not Disturb on. You never pick a route yourself.
- **Sender names are approved by each network separately**, and it takes weeks. Until a network approves yours, its recipients see the shared name, or are skipped if you prefer.

Base URL`https://api.dbrij.com/api/v1`

## Quickstart

Create an API key in **Developer, API keys** with the `sms:send` and `sms:read` scopes, buy units in **Ping, Units**, and accept the acceptable use terms in **Ping, Settings**. Then:

Send a message

```
curl https://api.dbrij.com/api/v1/ping/messages \
  -H "Authorization: Bearer <keyId>:<secret>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-4821-receipt" \
  -d '{ "to": ["08031234567"], "type": "transactional", "body": "Your payment of N25,000 was received." }'
```

The response carries a Dbrij message id. Keep it: it is how you look the message up, and it is what your webhook is told about. The message is `queued` at this point. It becomes `submitted` within seconds, and `delivered` when the phone receives it.

## Message kinds and routes

Every send says what the message is with `type`. Ping decides the route from that and from what the sender name is registered for. You cannot choose a route directly, on purpose: the wrong one gets messages blocked and gets sender names banned by the networks.

| type | Use it for | Behaviour | Units a part |
| --- | --- | --- | --- |
| otp | Sign in and verification codes | Its own fast lane, ahead of all other traffic. Reaches Do Not Disturb phones, at any hour. | 5 |
| transactional | Receipts, alerts, reminders, results | Reaches Do Not Disturb phones, at any hour. Screened: marketing wording is refused. | 5 |
| promotional | Offers and announcements | Not delivered to Do Not Disturb phones. Not delivered to MTN between 8pm and 8am Lagos time, so those are held until 8am. Needs a promotional sender name of your own. | 4 |

A sender name is registered as transactional or promotional and can only carry its own kind. Sending `promotional` under a transactional name, or a code under a promotional one, is refused with a 400 that says which name to use instead.

**Parts.** Plain text is 160 characters a part. One character outside the basic set, including `;` `^` `{` `}` `[` `]` `~` `|` `\` and quote marks, or any emoji, makes the whole message 70 a part. The count is fixed when you send and is returned as `parts`.

POST`/ping/messages`sms:send

Send a message

Queue one message to up to 1,000 numbers. Units are held the moment it is accepted, so a send you cannot afford fails here with 402 and nothing is queued. Send an Idempotency-Key header and a retry of the same request returns the original message instead of sending twice.

PATH / BODY PARAMETERS

|  |  |  |
| --- | --- | --- |
| to* | string[] | Phone numbers. Nigerian numbers may be local (08031234567) or international (2348031234567, with or without a plus). Anything that is not a phone number is rejected. |
| body* | string | The text. Up to 6 parts. |
| type* | "otp" \| "transactional" \| "promotional" | What the message IS. The route is decided on our side from this and from what the sender name is registered for. See Message kinds and routes. |
| senderId | string | One of your sender names, exactly as registered. Omit it to send under the shared Dbrij name. |
| scheduledAt | string | ISO time to send at. Omit to send now. |

Request

```
curl -X POST https://api.dbrij.com/api/ping/messages \
  -H "Authorization: Bearer $DBRIJ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "to": ["08031234567"],
  "type": "transactional",
  "senderId": "ConfidMFB",
  "body": "Your payment of N25,000 was received. Thank you."
}'
```

Request body

```
{
  "to": ["08031234567"],
  "type": "transactional",
  "senderId": "ConfidMFB",
  "body": "Your payment of N25,000 was received. Thank you."
}
```

Response 200

```
{
  "success": true,
  "data": {
    "messages": [ {
      "id": "0b7c1d8e-3f2a-4c61-9a57-6d1f0e2b8c34",
      "to": "2348031234567",
      "network": "mtn",
      "senderId": "ConfidMFB",
      "route": "transactional",
      "classification": "transactional",
      "body": "Your payment of N25,000 was received. Thank you.",
      "encoding": "gsm",
      "parts": 1,
      "units": 5,
      "status": "queued",
      "statusLabel": "Queued",
      "reason": null,
      "campaignId": null,
      "createdAt": "2026-09-19T10:04:11.000Z",
      "submittedAt": null,
      "finalAt": null
    } ],
    "campaignId": null,
    "units": 5
  }
}
```

## What comes back, and what can refuse

- `201` the message is queued and its units are held.
- `402 ping_units_required` not enough units. The body carries `needed` and `available`. Nothing was queued.
- `429 ping_rate_limited` more messages, or more different numbers, in a minute than this workspace allows. Large sends belong in a campaign, which paces itself.
- `403 ping_verification_required` the send would go out under a shared Dbrij sender name and the account has not been verified yet. Get verified once under Sender names in the dashboard, and you are given one shared name. That is the only shared name the account can send under, and the only one `GET /ping/sender-ids` lists. Sends under a sender name of your own that a network has approved are not affected.
- `403` the workspace is suspended, has not accepted the terms, or the number is outside Nigeria while international sending is off.
- `400` a malformed number, a message over 6 parts, a kind the sender name cannot carry, or marketing wording on the transactional route.
- `503` sending is briefly paused on our side. Nothing was charged. Retry with the same Idempotency-Key.

More than 20 recipients in one call becomes a campaign: you get a `campaignId` back, and the messages are sent in paced batches.

GET`/ping/messages/:id`sms:read

Get one message

The message and where it is in its life. The id is the Dbrij id returned when you sent it.

Request

```
curl -X GET https://api.dbrij.com/api/ping/messages/:id \
  -H "Authorization: Bearer $DBRIJ_API_KEY"
```

Response 200

```
{
  "success": true,
  "data": {
    "id": "0b7c1d8e-3f2a-4c61-9a57-6d1f0e2b8c34",
    "to": "2348031234567",
    "network": "mtn",
    "senderId": "ConfidMFB",
    "route": "transactional",
    "classification": "transactional",
    "body": "Your payment of N25,000 was received. Thank you.",
    "encoding": "gsm",
    "parts": 1,
    "units": 5,
    "status": "delivered",
    "statusLabel": "Delivered",
    "reason": null,
    "campaignId": null,
    "createdAt": "2026-09-19T10:04:11.000Z",
    "submittedAt": null,
    "finalAt": null
  }
}
```

GET`/ping/messages`sms:read

List messages

Newest first. Page backwards by passing the createdAt of the last row you have as before.

QUERY PARAMETERS

|  |  |  |
| --- | --- | --- |
| status | string | Only messages in this status. |
| before | string | ISO time. Returns messages created before it. |
| limit | number | Up to 200. Defaults to 50. |

Request

```
curl -X GET https://api.dbrij.com/api/ping/messages \
  -H "Authorization: Bearer $DBRIJ_API_KEY"
```

Response 200

```
{
  "success": true,
  "data": [ {
      "id": "0b7c1d8e-3f2a-4c61-9a57-6d1f0e2b8c34",
      "to": "2348031234567",
      "network": "mtn",
      "senderId": "ConfidMFB",
      "route": "transactional",
      "classification": "transactional",
      "body": "Your payment of N25,000 was received. Thank you.",
      "encoding": "gsm",
      "parts": 1,
      "units": 5,
      "status": "queued",
      "statusLabel": "Queued",
      "reason": null,
      "campaignId": null,
      "createdAt": "2026-09-19T10:04:11.000Z",
      "submittedAt": null,
      "finalAt": null
    } ]
}
```

GET`/ping/balance`sms:read

Your unit balance

The units you can spend now, the units held for messages still in the queue, and what a message part costs on each route.

Request

```
curl -X GET https://api.dbrij.com/api/ping/balance \
  -H "Authorization: Bearer $DBRIJ_API_KEY"
```

Response 200

```
{
  "success": true,
  "data": {
    "units": 18450,
    "held": 25,
    "unitsPerPart": { "transactional": 5, "promotional": 4 }
  }
}
```

## Statuses

`queued`Accepted and waiting its turn. Units are held.

`held`Waiting for 8am Lagos time under the overnight rule, or for its scheduled time. Units are held.

`submitted`Handed to the network. This is the moment you are charged.

`delivered`Reached the handset.

`undelivered`The network could not reach the phone. Charged.

`blocked`The number has Do Not Disturb on, or the network refused it. Charged.

`expired`The phone stayed off or out of signal until the message expired. Charged.

`unconfirmed`Sent, and the network never reported what happened. We stop waiting after 24 hours. Charged.

`failed`Never left Dbrij. Not charged; the held units went back to your balance.

`skipped`Not sent, by your own setting: the network has not approved your sender name. Not charged.

When a message did not arrive, `reason` says why in plain words. A final status never changes again, with one exception: `unconfirmed` becomes the real answer if the network reports late.

GET`/ping/sender-ids`sms:read

List sender names

Your sender names and the shared Dbrij ones, each with its approval on every network. Registering a name is done in the dashboard, because it needs documents.

Request

```
curl -X GET https://api.dbrij.com/api/ping/sender-ids \
  -H "Authorization: Bearer $DBRIJ_API_KEY"
```

Response 200

```
{
  "success": true,
  "data": [
    {
      "senderId": "ConfidMFB",
      "route": "transactional",
      "status": "partly_approved",
      "networks": { "mtn": "approved", "airtel": "approved", "glo": "pending", "9mobile": "pending" },
      "shared": false
    }
  ]
}
```

## How approval reaches your traffic

Each network approves a name on its own, so `networks` has four answers. For a recipient on a network that has not approved your name yet, Ping follows your workspace setting: send under the shared Dbrij name (the default), or skip. A skipped message comes back with status `skipped` and costs nothing.

A transactional name takes about 20 working days from the day the letters reach the networks; a promotional one about 2. Register early. It costs nothing to have a name waiting.

## Webhooks

Add an endpoint under **Developer, Webhooks**. Every delivery is signed with that app's own Dbrij secret: `X-Dbrij-Signature: sha256=…` is an HMAC SHA256 of the raw body, and `X-Dbrij-Signature-V2` adds a timestamp so a replayed delivery can be refused. Verify one before you trust the payload.

`sms.sent`The message was handed to the network. You have been charged.

data

```
{ "id", "to", "status": "submitted", "parts", "units", "senderId" }
```

`sms.delivered`The phone received it.

data

```
{ "id", "to", "status": "delivered", … }
```

`sms.failed`It did not arrive, or it never left Dbrij. status and reason say which.

data

```
{ "id", "to", "status", "reason", … }
```

The `id` is always the Dbrij message id you were given when you sent it. Match on that.

## Charging, limits and errors

- **Units.** Bought in advance in the dashboard, from your Dbrij wallet or by card. They never expire. There is no credit and no post paid billing.
- **Charged at sending.** Units are held when a message is accepted and taken when it is handed to the network. There is no refund for a message that did not arrive, because the networks charge for it either way.
- **New workspaces** send to 5 numbers at a time until their first purchase and the terms are accepted.
- **Speed.** 120 messages and 60 different numbers a minute by default on direct sends. Campaigns are paced separately and are not bound by this. Ask us to raise it for genuine traffic.
- **International** sending is off by default and switched on by Dbrij on request.
- **Idempotency.** Send an `Idempotency-Key` header on every request you might retry. The same key returns the same message.
- **Errors** use the platform envelope: `{ "success": false, "statusCode", "message", "code" }`. The message is written to be shown to a person.
