API Documentation
Everything you need to integrate Vertex into your stack. Send messages, verify OTPs, configure webhooks, and manage your account — all through a single RESTful API.
Quick Start#
Vertex provides a RESTful JSON API. All requests must be made over HTTPS. Authenticate using your API key via the Authorization header. The base URL for all API requests is:
https://api.vertex.pk/v1Below is a complete example of sending an SMS message using the Vertex API. Replace the placeholder API key with your own live or test key.
curl -X POST https://api.vertex.pk/v1/messages \
-H "Authorization: Bearer vx_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"from": "VERTEX",
"content": "Your OTP is 492013",
"channel": "sms"
}'Vertex responds with a JSON payload containing the message ID and current status. All message IDs are prefixed with msg_ and are globally unique.
Setup & Installation#
Install the Vertex SDK for your preferred language using the package manager of your choice:
npm install @vertex/node-sdkpip install vertex-clientcomposer require vertex/vertex-phpcomposer require vertex/laravelSandbox Environment#
Every Vertex account comes with a fully-featured sandbox environment. Sandbox API keys begin with vx_test_ and behave identically to production keys except that no real messages are sent. Delivery receipts are simulated after a configurable delay.
import Vertex from "@vertex/node-sdk";
// Use your sandbox key — messages will not be delivered
const vertex = new Vertex("vx_test_your_sandbox_key_here");
// Optional: simulate DLR after 2 seconds
vertex.configure({ sandbox: { dlrDelayMs: 2000 } });
// All other API methods work identically to productionAuthentication#
Vertex uses API keys to authenticate requests. You can manage your API keys from the Vertex Dashboard. Each key is prefixed to indicate its environment:
Include the API key in all requests as a bearer token in theAuthorization header:
Security Best Practices#
Store API keys securely. Never expose them in client-side code, version control, or logs. Use environment variables or a secrets manager in production. Rotate keys regularly and use restricted keys with minimal required permissions using Vertex's RBAC system.
All API requests must be made over TLS 1.2 or higher. Requests made over plain HTTP will be rejected. Vertex supports IP allow-listing at the account level — configure trusted IPs in the dashboard.
REST API#
The Vertex API is organised around REST. All requests must be made to the production base URL. We return JSON-encoded responses and use standard HTTP response codes to indicate success or failure.
Endpoints#
Below is a complete list of available REST endpoints. All paths are relative tohttps://api.vertex.pk.
| Method | Path | Description |
|---|---|---|
| GET | /v1/messages | List all messages |
| POST | /v1/messages | Send a new message |
| GET | /v1/messages/:id | Retrieve a message |
| DELETE | /v1/messages/:id | Cancel a queued message |
| POST | /v1/otp/generate | Generate an OTP |
| POST | /v1/otp/verify | Verify an OTP code |
| GET | /v1/accounts | Get account details |
| GET | /v1/balance | Check wallet balance |
| GET | /v1/senders | List sender IDs |
| POST | /v1/senders | Register a sender ID |
| GET | /v1/webhooks | List webhook endpoints |
| POST | /v1/webhooks | Create a webhook endpoint |
| PUT | /v1/webhooks/:id | Update a webhook endpoint |
| DELETE | /v1/webhooks/:id | Delete a webhook endpoint |
Pagination#
List endpoints return paginated results using cursor-based pagination. The response includes a cursor field — pass its value as the starting_after parameter to fetch the next page.
// Response
{
"data": [ ... ],
"cursor": "msg_01JABCDEFGHIJKLMNOPQRST",
"has_more": true
}
// Next request
GET /v1/messages?starting_after=msg_01JABCDEFGHIJKLMNOPQRST&limit=50Idempotency#
Vertex supports idempotent requests to safely retry API calls without duplicate side-effects. Send an Idempotency-Keyheader with a unique UUID for each request. Vertex stores the response for that key for 24 hours and returns it for any subsequent requests with the same key.
SMS API#
The SMS API lets you send branded, transactional, and promotional messages across all major Pakistani operators. Messages are routed through Vertex's carrier-grade SMPP infrastructure with automatic failover.
Send SMS#
Send a text message to a single recipient. The tofield must be in E.164 format. For branded SMS, frommust be an approved sender ID.
sms (default), whatsappcurl -X POST https://api.vertex.pk/v1/messages \
-H "Authorization: Bearer vx_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"from": "VERTEX",
"content": "Your OTP is 492013",
"channel": "sms"
}'A successful response returns the message ID and current status:
{
"id": "msg_01JABCDEFGHIJKLMNOPQRST",
"status": "queued",
"to": "+923001234567",
"from": "VERTEX",
"channel": "sms",
"content": "Your OTP is 492013",
"parts": 1,
"credits": 0.85,
"created_at": "2026-07-22T10:30:00.000Z"
}DLR Callback#
Delivery receipts (DLR) are sent to your configured webhook endpoint. Each DLR includes the message ID, the delivery status, operator details, and timestamps. You can also poll the GET /v1/messages/:idendpoint for the latest status.
Sender IDs#
Branded sender IDs must be registered and approved before use. The approval process typically takes 1–2 business days. Submit sender ID registration requests via the Vertex Dashboardor the POST /v1/senders API endpoint.
OTP API#
Vertex's OTP API provides sub-second generation and verification with intelligent fallback across SMS, WhatsApp, and voice channels. OTPs are single-use, time-limited, and automatically expire.
Generate OTP#
Generate and send a one-time passcode to a recipient. The OTP code is auto-generated and sent via the specified channel. The response contains the otp_id which you must store to verify later.
sms, whatsapp, voicecurl -X POST https://api.vertex.pk/v1/otp/generate \
-H "Authorization: Bearer vx_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"to": "+923001234567",
"channel": "sms",
"length": 6,
"expiry_minutes": 5
}'Response contains the OTP ID and delivery status:
{
"otp_id": "otp_01JABCDEFGHIJKLMNOPQRST",
"status": "sent",
"to": "+923001234567",
"channel": "sms",
"expires_at": "2026-07-22T10:35:00.000Z"
}Verify OTP#
Verify the code provided by the user against the generated OTP. Each OTP can be verified a limited number of times (default: 3 attempts) before it is locked. Once verified or expired, the OTP cannot be reused.
curl -X POST https://api.vertex.pk/v1/otp/verify \
-H "Authorization: Bearer vx_live_abc123def456" \
-H "Content-Type: application/json" \
-d '{
"otp_id": "otp_01JABCDEFGHIJKLMNOPQRST",
"code": "492013"
}'The response indicates whether verification succeeded:
// Success
{
"valid": true,
"otp_id": "otp_01JABCDEFGHIJKLMNOPQRST",
"verified_at": "2026-07-22T10:31:15.000Z"
}
// Failure
{
"valid": false,
"reason": "invalid_code",
"attempts_remaining": 2
}Webhook API#
Webhooks allow you to receive real-time notifications about message delivery status changes, OTP events, and account alerts. Configure webhook endpoints in the dashboard or via the API. Each endpoint can subscribe to specific event types.
Events#
Vertex sends webhook events as HTTP POST requests to your configured URL. Each event payload includes the event type, a unique ID, and the associated data object.
| Event | Description |
|---|---|
| message.sent | Message accepted by operator and sent to recipient. |
| message.delivered | Message confirmed as delivered to the handset (DLR received). |
| message.failed | Message delivery failed. Check failure_reason for details. |
| message.queued | Message has been queued for delivery. |
| otp.generated | An OTP has been generated and sent to the recipient. |
| otp.verified | An OTP has been successfully verified. |
| otp.expired | An OTP has expired without verification. |
| balance.low | Wallet balance is below the configured threshold. |
{ id, type, data, created_at } structure. The data field contains the relevant object (message, OTP, etc.).Webhook Signing#
Every webhook request includes a Vertex-Signatureheader. This is an HMAC-SHA256 signature of the raw request body, signed with your webhook signing secret. Always verify this signature before processing the event to ensure the request is genuinely from Vertex.
import crypto from "crypto";
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express / Fastify middleware example
app.post("/webhooks/vertex", (req, res) => {
const sig = req.headers["vertex-signature"];
const secret = process.env.VERTEX_WEBHOOK_SECRET;
if (!verifyWebhook(JSON.stringify(req.body), sig, secret)) {
return res.status(401).send("Invalid signature");
}
// Process the event
res.status(200).send("OK");
});Retries#
If your webhook endpoint does not return a 2xx status within 5 seconds, Vertex will retry the delivery with exponential backoff. The retry schedule is: 10s, 30s, 2m, 10m, 30m, 2h, 6h, 24h. After 24 hours of continuous failure, the event is dropped and logged. You can replay failed events from the dashboard.
id field that is unique per event. Use it to deduplicate deliveries in case of retries.Error Codes#
The Vertex API uses standard HTTP status codes to indicate success or failure. Error responses include a machine-readable error_codeand a human-readable message.
| Error Code | HTTP | Description |
|---|---|---|
invalid_credentials | 401 | API key is missing, invalid, or expired. |
insufficient_balance | 402 | Wallet balance is too low to send the message. |
invalid_recipient | 400 | The recipient phone number is invalid or not E.164 formatted. |
invalid_sender | 400 | The sender ID is invalid, unregistered, or not approved. |
rate_limit_exceeded | 429 | Request rate exceeds the allowed limit for your plan. |
duplicate_request | 409 | A request with the same idempotency key already exists. |
channel_not_enabled | 403 | The specified channel is not enabled for your account. |
invalid_template | 400 | The template name does not exist or params do not match. |
otp_expired | 400 | The OTP has expired. Generate a new one. |
otp_already_verified | 400 | This OTP has already been verified. |
too_many_attempts | 429 | Too many OTP verification attempts. Try again later. |
internal_error | 500 | An unexpected error occurred. Contact support if persistent. |
{ error_code, message, request_id }. Include the request_id when contacting support.Rate Limits#
Rate limits protect the API from abuse and ensure fair usage across all customers. Limits are applied per API key and are reset every minute.
When a rate limit is exceeded, the API returns a 429status code with the rate_limit_exceeded error code. The response includes Retry-After header indicating the number of seconds to wait before retrying.
HTTP/1.1 429 Too Many Requests
Content-Type: application/json
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1721655000
Retry-After: 12
{
"error_code": "rate_limit_exceeded",
"message": "Rate limit exceeded. Try again in 12 seconds.",
"request_id": "req_01JABCDEFGHIJKLMNOPQRST"
}SDKs & Libraries#
Vertex provides official client libraries for the most popular programming languages. All SDKs are open-source, type-safe, and published on their respective package registries.
@vertex/node-sdknpm install @vertex/node-sdkvertex-clientpip install vertex-clientvertex/vertex-phpcomposer require vertex/vertex-phpvertex/laravelcomposer require vertex/laravelgithub.com/vertexpk/go-vertexgo get github.com/vertexpk/go-vertexcom.vertex:vertex-javagradle: com.vertex:vertex-java:1.0.0Vertex.Sdkdotnet add package Vertex.Sdkvertex-rubygem install vertex-ruby