Skip to content

Direct SMS

Direct SMS puts the rendered content in the request. Reach for it when your application already owns the copy; reach for message definitions when non-developers should change copy without a deploy.

const response = await fabric.sms.send(
{
to: "+233201234567",
senderId: "FABRIC",
body: "Your order GH-4821 has shipped.",
currency: "GHS",
class: "transactional",
},
{ idempotencyKey: "order-shipped:GH-4821" },
);
const sms = response.data; // { id, status, encoding, segments, cost }
Field Required Notes
to yes E.164 (+233…). Rejected client-side if malformed.
senderId yes An active sender name for the recipient’s country on live.
body yes The message text.
currency no Defaults to GHS. One of GHS, NGN, USD.
class no transactional (default) or promotional.

An idempotency key is optional on a single send but strongly recommended — without one, a retried request is not replayed and may send twice.

Fabric reports how it encoded your text and how many segments it billed.

  • gsm7 — the standard 7-bit alphabet. 160 characters per single segment.
  • ucs2 — used when the body contains any character outside GSM-7 (many emoji, some accented characters). This drops the single-segment limit to 70 characters.

A long message is split into multiple billed segments. response.data.segments is the count you are charged for, and response.data.cost is the total for the send. Keep transactional copy inside one GSM-7 segment when cost matters.

Submit 1–100 items in one request. Each item carries its own clientReference, and the batch requires an idempotency key.

const batch = await fabric.sms.sendBatch(
[
{ clientReference: "row-1", to: "+233201234567", senderId: "FABRIC", body: "Hi Ama" },
{ clientReference: "row-2", to: "+2348012345678", senderId: "FABRIC", body: "Hi Kwame" },
],
{ idempotencyKey: "welcome-blast:2026-07-23" },
);
for (const item of batch.data.items) {
console.log(item.clientReference, item.status, item.messageId, item.errorCode);
}

The batch resource reports status (processing | completed), totalCount, acceptedCount, failedCount, and a per-item items[] array. One bad item does not fail the batch — check each item’s status and errorCode. Retrieve it again later with fabric.sms.retrieveBatch(id).

const detail = await fabric.sms.retrieve(sms.id);
console.log(detail.data.status, detail.data.timeline, detail.data.failureReason);
const page = await fabric.sms.list({ limit: 50 });
console.log(page.data.items.length, page.data.nextCursor);
for await (const summary of fabric.sms.iterate()) {
console.log(summary.id, summary.status);
}

retrieve returns a MessageDetail with the delivery timeline, senderId, and — unless the record is redacted — the body. list is cursor-paginated (limit 1–100, default 50) and returns MessageSummary rows, each tagged with its deliveryMode (virtual in sandbox, live in production); iterate follows nextCursor across pages for you.

  1. Send with an idempotency key.
  2. Follow the terminal state through a webhook, not the send response.
  3. Reconcile cost against your wallet.