Text your agent in 10 minutes
Create an agent in the nmbr app, then make it talk back from a terminal — with curl or the TypeScript SDK.
You need: the nmbr app (iOS, Android or web), and a machine with outbound internet — a laptop is fine, nothing has to be reachable from the internet.
1. Create the agent (in the app) Available
Agents → Yours → Create an agent. Give it a name. nmbr assigns a random 800-xxx-xxx nmbr, adds you as its first contact, and shows its token once. Copy it: it starts with agent:.
export TOKEN="agent:…"
Lost it? Open the agent (Agents → Yours → your agent) and mint a new token; revoke the old one there too.
2. Say hello to yourself
curl -s https://nmbr.ai/api/agent/v1/me -H "Authorization: Bearer $TOKEN"
# {"id":"…","nmbr":"800-123-456","displayName":"My Agent",…}
curl -s -X POST https://nmbr.ai/api/agent/v1/messages \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"to":"123-456-789","type":"text","content":"hello from my laptop"}'
Replace 123-456-789 with your own nmbr. Your phone buzzes. Reply to it in the app.
3. Receive the reply (long-poll)
SEQ=$(curl -s https://nmbr.ai/api/agent/v1/updates/cursor -H "Authorization: Bearer $TOKEN" | jq .seq)
curl -s "https://nmbr.ai/api/agent/v1/updates?afterSeq=$SEQ&wait=25" -H "Authorization: Bearer $TOKEN"
The request holds up to 25 s and returns as soon as something happens:
{ "events": [ { "id": "…", "seq": 42, "type": "message.received", "ts": "…", "agentId": "…",
"payload": { "conversationId": "…", "from": { "nmbr": "123-456-789", "displayName": "You" },
"message": { "id": "…", "type": "text", "content": "hi!" } } } ],
"nextSeq": 42 }
Loop with afterSeq=nextSeq. That's the whole inbound story — no webhook, no tunnel.
4. An echo agent with the SDK Available
npm i @nmbrai/sdk
import { NmbrAgent } from "@nmbrai/sdk";
const agent = new NmbrAgent({ token: process.env.TOKEN! });
for await (const event of agent.updates()) { // long-polls forever, resumes after errors
if (event.type === "message.received") {
const { conversationId, message } = event.payload as any;
await agent.setTyping(conversationId);
await agent.sendText({ conversationId }, `You said: ${message.content}`);
}
}
Run it with node --env-file=.env echo.mjs (Node ≥ 18). Text your agent. It answers.
Swap the echo for an LLM call and you have a personal assistant on your phone that runs on your hardware. Voice notes arrive with message.transcript already filled in.
5. Ask before acting Available
const decision = await agent.proposeAndWait({
to: "123-456-789",
kind: "deploy",
title: "Deploy v2 to prod?",
payload: { ref: "abc123" },
});
if (decision.state === "approved") deploy(decision.editedPayload ?? decision.payload);
// "rejected" and "expired" both mean: do not act.
Your phone shows an approval card with Approve / Edit / Reject. Nothing runs on nmbr's side — ever. See Approvals.
Next
Skip the typing: the agent template has all of the above as runnable files (
npm run echo,npm run agent,npm run approvals,npm run webhook).Prefer push over polling? Set a webhook — deliveries are signed, and the SDK's
receiveWebhookverifies them by default.Read Security before you give an agent real powers.
API reference for everything else: conversations, history, reactions, read receipts, profile.