Call Bullet

Send SMS from an AI-built app

To send SMS from an AI-built app, create a Call Bullet key with sms:send, call GET /sms/readiness, send a free test: true message, then send live texts with POST /sms/messages. Each text returns an ID with a delivery timeline, and failed texts refund their credit automatically.

Last updated · Call Bullet

What do I need before sending a real text?

You need a workspace, an approved sender and SMS credits. The readiness endpoint tells your app which of these is missing and what to do next, so the app never guesses.

Every workspace starts with 25 free credits. A sender name such as your brand is reviewed by Call Bullet, usually within one working day. While it is pending you can send simulated tests, or free live tests to a mobile you have verified.

ModeWhat happensCost
test: trueSimulated; nothing reaches a phoneFree
test: "live"Real text to a verified test mobile, prefixed [Test]Free, 20 a day per workspace
LiveReal text from your approved sender1 credit per UK segment

How do I check readiness?

Call readiness first and show next_step to the user. It covers sender approval, credits, templates and webhooks.

TypeScript · readiness check
import { cb } from "./call-bullet.server";

export async function smsReady(workspaceId: string) {
  const { data } = await cb<{ data: { live_ready: boolean; test_ready: boolean; next_step: string; credits: number } }>(
    `/sms/readiness?workspace_id=${workspaceId}`,
  );
  return data; // e.g. { live_ready: false, next_step: "Sender is waiting for review…" }
}

How do I send a text?

Send from a server function with an Idempotency-Key, so a retry never sends the same text twice.

TypeScript · send an SMS
import { cb } from "./call-bullet.server";

export async function sendSms(opts: {
  workspaceId: string; senderId: string; to: string; body: string; ref: string; test?: boolean | "live";
}) {
  const { data } = await cb<{ data: { id: string; status: string; segments: number; credits_used: number } }>(
    "/sms/messages",
    {
      method: "POST",
      headers: { "Idempotency-Key": opts.ref }, // e.g. "job-1042-booked"
      body: JSON.stringify({
        workspace_id: opts.workspaceId,
        sender_id: opts.senderId,
        to: opts.to,                 // +447700900123 or 07700 900123
        body: opts.body,
        category: "transactional",
        external_ref: opts.ref,
        test: opts.test ?? true,     // start simulated
      }),
    },
  );
  return data; // store data.id on your job or booking
}

Get the sender_id from GET /sms/senders?workspace_id=…. Start with test: true, switch to "live" for your own phone, and drop the flag only when the sender is approved.

How are texts charged?

Each UK segment costs one credit. A plain text of up to 160 characters is one segment; longer texts or ones with emoji split into more. The send response returns segments and credits_used, and GET /sms/balance shows what is left.

Credit packs and subscriptions are on the pricing page. Failed and expired texts are refunded to the ledger, which you can read with GET /sms/credits/ledger.

What UK rules apply to SMS?

Transactional texts, such as booking confirmations and job updates, can be sent to customers who expect them. Marketing texts need recorded consent under PECR, a way to opt out, and are only sent between 8am and 9pm UK time. Call Bullet enforces opt-outs and returns opted_out or compliance_blocked instead of sending.

Sender names cannot receive replies. If customers need to reply, use a reply-capable number as the sender. The ICO's guidance on electronic marketing explains consent in detail.

How do I handle errors?

Each error has a code your app can act on.

  • 402 insufficient_credits: show a link to buy credits.
  • 422 sender_not_ready: keep using test sends and check readiness.
  • 422 opted_out: mark the contact as opted out and stop texting them.
  • 422 compliance_blocked: marketing outside 8am–9pm or without consent.
  • 422 missing_variables: a template variable was empty.

For delivery proof without polling, add SMS delivery webhooks. The full reference is the SMS API guide.

What prompt do I paste into my AI app builder?

This prompt builds a safe send path with test mode first.

AI build prompt · send SMS
Add UK SMS to this app with Call Bullet.
- Docs: https://callbullet.com/developers/api/sms and https://callbullet.com/developers/api.md. Use only documented endpoints.
- Store my key (cb_live_…, scopes sms:read and sms:send) as the server secret CALL_BULLET_API_KEY.
- On the settings page call GET /sms/readiness?workspace_id=… and show next_step in plain words.
- Create server function sendSms(workspaceId, senderId, to, body, ref) calling POST /sms/messages with Idempotency-Key = ref, external_ref = ref and category "transactional".
- Default to test:true until I say the sender is approved. Store the returned message id on the record.
- Handle 402 and 422 error codes with clear messages. Never send marketing texts without recorded consent.

How long does it take end to end?

About ten minutes to a simulated text, and a few more to a real test on your own phone after verifying it with a six-digit code. Live customer texts follow sender approval, usually within one working day. A small business in Rochdale sending booking confirmations can be fully live the next morning.

How do I send texts from templates?

Templates keep wording consistent and let the API check every variable before a text is sent.

Create a template once with POST /sms/templates, using variables such as {{customer_name}} and {{job_ref}}. When sending, pass template_id and a variables object instead of body. If any variable is missing or empty, the API returns missing_variables and lists them, so a half-filled text never reaches a customer. Keep templates under 160 plain characters to stay at one credit per text; the template response includes estimated_segments so you can check before you send.

Templates also make reporting easier. GET /sms/usage can group texts by template, so you can see that booking confirmations deliver at 99% while reminder texts are where failures cluster.

How do I show an accountable SMS log in my app?

Store the Call Bullet message ID and your own reference on every record you text about, and build a log from GET /sms/messages.

A good SMS log answers the question “what did we send this customer, and did it arrive?” without anyone opening another system. List messages newest first with date and time in UK time, recipient, the linked job or customer in your app, template, status badge and credits used. Clicking a row opens the delivery timeline from GET /sms/messages/{id}: queued, sent, delivered or failed, with any error in plain words.

Filter by phone number, reference, campaign or status, all supported as query parameters. Clearly label test messages so nobody mistakes a simulated send for a real one. For running costs, read GET /sms/usage grouped by day and show credits used alongside the balance.

How do I text several brands from one app?

Give each brand its own workspace, sender and templates, and let one key cover them all.

An owner running three businesses from one app should still keep each brand in its own Call Bullet workspace. Each workspace has its own sender name, templates, credits and usage, so a garage's texts never appear under a tyre shop's name and each brand's costs are clear. Call GET /sms/workspaces to list every brand the key can text for, with a readiness summary for each.

In your app, add a brand switcher at the top of the SMS screens. Every send passes the selected workspace ID and that workspace's sender ID, and the log, usage and balance views read from the same workspace. This keeps accounting clean and means approving or pausing one brand never affects the others.

  • Use one workspace per brand or company.
  • List brands with GET /sms/workspaces.
  • Pass the matching workspace_id and sender_id on every send.
  • Show usage and balance per brand.

How long does it take from first request to first live text?

Allow about ten minutes for setup and one working day for sender approval.

Creating a key, saving it as a secret and running the readiness check takes a few minutes. A simulated send follows straight away. Verifying your own mobile for live tests takes a minute more: request a code with POST /sms/test-mobiles, enter the six digits with POST /sms/test-mobiles/verify, and send with test: "live". The text arrives from the shared Call Bullet sender, prefixed with [Test] and your brand name. Once the sender name is approved you receive the sms.sender.approved webhook, and your app can switch to live sending automatically.

Key takeaways

  • Check GET /sms/readiness before sending and follow next_step.
  • Send with POST /sms/messages and an Idempotency-Key.
  • Start with test:true; live tests to verified mobiles are free.
  • One credit per UK segment; failed texts are refunded.
  • Marketing texts need consent and 8am–9pm UK sending.

Frequently asked questions

Can I send a text without a sender approved?

Only tests: simulated with test:true, or free live tests to a verified mobile with test:"live".

How do I stop duplicate texts?

Send an Idempotency-Key header. A retry with the same key returns the original message.

Can customers reply?

Not to a sender name. Use a reply-capable number as the sender if you need replies.

How do I know a text arrived?

Read GET /sms/messages/{id} for its timeline, or receive the sms.delivered webhook.

Are failed texts charged?

No. Failed and expired texts refund their credits automatically.

Can I use templates?

Yes. Send template_id with variables, and the API checks every variable is filled before sending.

See which marketing makes your phone ring

Start with one UK tracking number or a full website pool. Every call is recorded, transcribed and traced back to its source.