API documentation

A single REST API for transactional email and SMS. Every endpoint returns JSON with a request id, so you can trace any send end to end.

Quickstart

Create a workspace, generate a test API key from Developers → API keys, then send your first message. Test keys never touch a real provider — messages are simulated and appear in your logs marked as test.

bash
curl -X POST https://api.getquiver.app/api/public/v1/email/send \
  -H "Authorization: Bearer qv_test_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "customer@example.com",
    "subject": "Your order is confirmed",
    "html": "<p>Thanks for your order!</p>"
  }'

Authentication

Pass your API key as a bearer token on every request. Keys are scoped to one workspace and one environment.

bash
Authorization: Bearer qv_live_xxxxxxxxxxxxxxxx
  • qv_test_* — simulated delivery, no credits consumed.
  • qv_live_* — real delivery through Resend and Twilio.
  • Keys are shown once at creation and stored only as a hash. Rotate freely.

Send email

POST/api/public/v1/email/send

Provide html, text, or a template_id. Template variables use {{variable}} syntax.

json
{
  "to": "customer@example.com",
  "from": "orders@yourdomain.com",
  "template_id": "order-confirmed",
  "variables": {
    "first_name": "Maria",
    "order_id": "1042",
    "order_total": "$38.50"
  },
  "idempotency_key": "order-1042-confirmed"
}
200 response
{
  "success": true,
  "request_id": "req_8f2c1d4b9a7e",
  "data": {
    "message": {
      "id": "5c0f...",
      "status": "sent",
      "channel": "email",
      "environment": "test"
    }
  }
}

Send SMS

POST/api/public/v1/sms/send

Numbers must be E.164 formatted. Contacts without SMS opt-in and numbers on your suppression list are rejected before the provider is called.

json
{
  "to": "+14155550142",
  "body": "Hi {{first_name}}, order {{order_id}} is ready for pickup.",
  "variables": { "first_name": "Maria", "order_id": "1042" }
}

Bulk sending

POST/api/public/v1/email/bulk
POST/api/public/v1/sms/bulk

Up to 1,000 recipients per request, each with its own variables. Sends are queued and retried with exponential backoff.

json
{
  "template_id": "weekly-specials",
  "subject": "This week at Bella's",
  "messages": [
    { "to": "maria@example.com", "variables": { "first_name": "Maria" } },
    { "to": "sam@example.com", "variables": { "first_name": "Sam" } }
  ]
}

Order notifications

POST/api/public/v1/notifications/order

One call fans out to email and SMS using your saved templates for that event, with a built-in fallback if no template exists yet.

json
{
  "event": "order_ready",
  "channels": ["email", "sms"],
  "customer": {
    "name": "Maria Lopez",
    "email": "maria@example.com",
    "phone": "+14155550142"
  },
  "order": { "id": "1042", "total": "$38.50" },
  "business_name": "Bella's Kitchen",
  "idempotency_key": "order-1042-ready"
}

Supported events: order_received, order_confirmed, order_processing, order_ready, order_shipped, order_delivered, payment_received, booking_confirmed, booking_cancelled, password_reset, welcome.

Contacts

POST/api/public/v1/contacts

Upserts by email or phone. Use it to keep your GetQuiver audience in sync with your own database.

json
{
  "email": "maria@example.com",
  "phone": "+14155550142",
  "first_name": "Maria",
  "tags": ["vip", "pickup"],
  "sms_opt_in": true
}

Webhooks

Delivery events from Resend and Twilio are ingested automatically and applied to the matching message, updating status and campaign counters.

POST/api/public/v1/webhooks/resend
POST/api/public/v1/webhooks/twilio

Signatures are verified with your provider secret, timestamps older than five minutes are rejected, and duplicate event ids are ignored — so retries are safe.

Errors & limits

Errors use a consistent envelope with a machine-readable code.

json
{
  "success": false,
  "request_id": "req_8f2c1d4b9a7e",
  "error": {
    "code": "validation_error",
    "message": "One or more fields are invalid.",
    "details": [{ "field": "to", "message": "Required" }]
  }
}
StatusCodeMeaning
401missing_api_keyNo bearer token supplied
401invalid_api_keyKey is unknown or revoked
402insufficient_creditsWorkspace is out of credits
422validation_errorRequest body failed validation
429rate_limitedOver 120 requests per minute
500internal_errorUnexpected failure — safe to retry

Rate limit: 120 requests per minute per API key. Retry after the window resets, or batch through the bulk endpoints.