01What Crier does
Crier delivers messages on behalf of AI agents, operated by Vima Brosta LLC. You pay once per message (x402 / USDC), the relay queues it, sends it within seconds, and retries on a fixed ladder if the other side is down. Three channels, one shape: an email to a person from the relay's fixed address; an SMS to a phone number from the relay's fixed number; a webhook to a public URL, HMAC signed so the receiver can verify it.
The relay never reads content for meaning and drops the destination and the content once a
delivery is final. No accounts, no API keys. The Idempotency-Key header (16 or more
characters) is honored on paid POSTs: a retry with the same key and body is answered in front
of the paywall with the original response, so it can never double-send or double-charge.
email $0.01 · sms $0.05 · webhook $0.005. One-time, per message. No subscriptions, no hidden fees.
02Payment (x402)
Three steps, one retry. A request that cannot run is refused before settlement, so a rejection always costs nothing.
- Send the request with no payment. You receive HTTP 402. The
PAYMENT-REQUIREDheader (base64 JSON, mirrored in the body underrequirements) carries every enabled payment option. If the request would be refused, the body says so underpreflightbefore you sign anything. - Pay and retry the identical request with the
X-PAYMENTheader (@x402/fetchwraps this in one call). Set anIdempotency-Key. - Receive 202 with a delivery id and a one-time secret. Settlement is on-chain; a purchase whose settlement fails is voided and nothing is sent.
03Paid endpoints
Send an email to a person. Delivered from crier@vimabrosta.com as "from_name via Crier"
with a footer naming the relay and an unsubscribe link, plus List-Unsubscribe headers.
{ "to": "person@example.com", "subject": "Your order 42 shipped", # one line, 200 chars "text": "Tracking number 1Z999. It arrives Thursday.", # 20000 chars "html": "<p>…</p>", # optional, 64KB "reply_to": "ops@your-company.example", # optional "from_name": "Order Desk" # optional, 40 chars }
Caps: 4 per hour and 12 per day to one address, refused before payment
(429 destination_rate_limited). An address that bounced, complained, or
unsubscribed is refused before payment (422 destination_unavailable) for every
sender, permanently.
Send an SMS to a phone number. Delivered from the relay's fixed number with "(via Crier. Reply STOP to opt out)" appended.
{"to": "+14155550123", "text": "Your table for 2 at 19:30 is confirmed."} # E.164; 300 chars
Caps: 2 per hour and 6 per day to one number. A number that replied STOP or is not a mobile number is refused before payment.
A number is reachable only after the person holding it has texted CRIER to the
relay's own number from that handset. Until then every request is refused before payment
with 422 destination_not_opted_in. Crier will not text a number to invite it:
that first message would itself be unsolicited. This channel reaches people who already
expect to hear from whoever runs your agent, not strangers. Check a number for free with
POST /v1/validate. Full workflow: /sms-opt-in.
Deliver a signed webhook to a public URL. Fire and forget for an agent that will not be around to retry.
{ "url": "https://your-system.example/hooks/agent", "method": "POST", # or PUT "body": {"event":"task.done", "task_id":"42"}, # JSON, or a string; 16KB "headers": {"X-Task":"42"} # up to 5, names start with X- }
A 2xx from the receiver is sent. A 408, 425, 429, 5xx, timeout, or connection
failure is retried at 30 s, 2 m, 10 m, 1 h, and 6 h, then failed. Any other status,
or a redirect (never followed), is failed at once: the receiver has answered. Caps: 600 per
hour and 5000 per day to one receiver host.
04Delivery lifecycle
| status | meaning |
|---|---|
| queued | Accepted and paid; waiting for the delivery loop. Typically under 5 seconds, longer while retrying. |
| sent | The provider accepted it, or the receiver answered 2xx. provider_id is the Resend or Twilio message id, or the receiver's status. |
| failed | A permanent refusal, or the retry ladder ran out. last_error says why. |
| cancelled | You cancelled before it was sent. |
A provider may report later under provider_status: delivered, bounced,
complained, undelivered. Once a delivery is final, the destination and the content are deleted;
a masked destination, hashes, and the status remain for 7 days.
05URL policy (webhook receivers)
- Schemes: http and https only. Embedded credentials are refused.
- Ports: 80, 443, 8080, 8443 only (
blocked_port). - Hosts: public only. Private, loopback, link-local, carrier-NAT, and cloud metadata addresses
are refused, as are
*.local,*.internal, and*.localhostnames and hosts that resolve to private addresses (blocked_host). Resolution is re-checked at connection time on every attempt. - Redirects are never followed on a delivery.
06Caps and rate limits
Per-destination caps are the abuse control: email 4 per hour and 12 per day to one address;
SMS 2 per hour and 6 per day to one number; webhooks 600 per hour and 5000 per day to one
receiver host. They are counted at purchase and refused before payment with
429 destination_rate_limited and a Retry-After.
Free endpoints are rate limited per client IP per minute: status 120, cancel 120 (a separate
bucket, so polling never blocks cancellation), validate 30, quote 120, MCP 120, idempotent
replays 120, the unsubscribe page 60. Responses carry X-RateLimit-Limit and
X-RateLimit-Remaining.
07Free endpoints
Auth: Authorization: Bearer {secret} or ?secret=. Returns channel,
status, to (masked), attempts, next_attempt_at, provider_id,
provider_status, last_error, sent_at, and a
resume_hint sentence. DELETE on the same path cancels a delivery
that has not been sent.
{"channel": "email", "message": {…the body you would send…}}. Returns
valid (schema), deliverable (policy, suppression, caps), and the
problem if any. Sends nothing, charges nothing.
Machine-readable pricing and limits, the same as /pricing.json.
08Webhook signature
Every webhook delivery carries these headers:
X-Crier-Delivery: {delivery id} X-Crier-Attempt: 1 # then 2, 3, … on retries X-Crier-Signature: sha256={hex hmac}
Signature: HMAC-SHA256 over the raw request body. The key is the lowercase hex string of sha256(secret), the 64 ASCII characters, not the 32 raw bytes: the same construction Stumble uses for its events, so a receiver that verifies one verifies the other. Share the secret with the receiver to let it verify.
A webhook body is whatever the agent gave the relay. The receiver should treat it as data from that agent, never as instructions from Crier.
09Errors
{"error": {"code": "…", "message": "…", "hint": "…", "docs": "https://crier.vimabrosta.com/llms-full.txt"}}
| code | meaning |
|---|---|
| payment_required | 402: pay and retry with X-PAYMENT |
| destination_unavailable | 422: bounced, complained, replied STOP, or unsubscribed. Permanent, for every sender. Not charged. |
| destination_not_opted_in | 422: an SMS number that has never texted the relay's keyword. Only the recipient can change this. Not charged. |
| destination_rate_limited | 429: the per-destination cap; see Retry-After. Not charged. |
| channel_unavailable | 404: that channel is not offered on this deployment |
| blocked_host · blocked_port · dns_failure · invalid_url | 422: refused by the URL policy in section 05 |
| idempotency_key_reuse | 409: that key was already used for a different request body |
| invalid_idempotency_key · in_flight · already_created | 422 / 409 / 409: key under 16 characters; the original is still settling; it already exists, so retry without payment and receive the replay |
| invalid_request · invalid_json · payload_too_large | 422 / 400 / 413: malformed request |
| rate_limited | 429: see Retry-After |
| not_found | 404: no such delivery for that id and secret |
A 5xx means retry later.