For developers
API & webhooks
Connect your website, store or CRM to Encore Text. The API and webhooks are included on every plan. The same rules apply as everywhere else in Encore Text: people are only added with consent, only subscribed contacts get texts, and STOP always works.
Getting started
- An owner or admin opens API & integrations in the app and creates a key with only the access it needs.
- Copy the key when it's shown — it's stored scrambled and can't be shown again. Revoke it anytime.
- Send it on every request as
Authorization: Bearer etk_…(or anX-Api-Keyheader). Keep keys on your server, never in a web page.
Base URL
https://encoretext.com/api/public/v1
curl https://encoretext.com/api/public/v1/me \ -H "Authorization: Bearer etk_your_key"
Access (scopes)
contacts:read— Look up contactscontacts:write— Add and update contacts, tags and opt-outsmessages:send— Text subscribed contactscampaigns:read— List campaigns and results
Requests and responses are JSON. Phone numbers can be any common US or Canadian format; responses use +1 format. Each key can make 120 requests a minute.
Endpoints
GET/me
Check that a key works.
Access: any key
Response
{ "workspace": { "id": "…", "name": "Northside Bakery" },
"key": { "name": "Website", "prefix": "etk_1a2b3c4d", "scopes": ["contacts:write"] } }GET/contacts?phone=+15551234567
Look up one contact by phone number.
Access: contacts:read
Response
{ "contact": { "id": "…", "phone": "+15551234567", "name": "Sam Lee", "email": null,
"tags": ["vip"], "custom_fields": {}, "status": "subscribed", "source": "api",
"subscribed_at": "2026-10-25T14:02:11Z", "opted_out_at": null, "created_at": "…" } }POST/contacts
Add a subscriber, or update an existing contact's name, email, tags and custom fields.
Access: contacts:write
Request body
{ "phone": "+15551234567", "name": "Sam Lee", "email": "sam@example.com",
"tags": ["website"], "custom_fields": { "favorite_item": "Rye loaf" },
"consent": true }Response
201 { "result": "subscribed", "contact": { … } }
200 { "result": "updated", "contact": { … } }- "consent": true is required to add someone new. Send it only when the person agreed to receive texts from you.
- Workspaces that text about alcohol also need "age_confirmed": true (21 or older) for new subscribers.
- Someone who opted out can't be re-added through the API (409 opted_out) — they can rejoin by texting START.
- Updating a contact never changes their consent.
POST/contacts/tags
Add or remove tags. Adding a tag can start a "tag added" automation.
Access: contacts:write
Request body
{ "phone": "+15551234567", "add": ["vip"], "remove": ["lead"] }Response
{ "contact_id": "…", "tags": ["website", "vip"] }POST/contacts/opt-out
Opt a number out. It gets no more texts and any automations stop.
Access: contacts:write
Request body
{ "phone": "+15551234567" }Response
{ "opted_out": true, "changed": true, "contact_id": "…" }POST/messages
Text one subscribed contact.
Access: messages:send
Request body
{ "phone": "+15551234567", "body": "Your order is ready for pickup today until 6 PM.", "track_links": true }Response
202 { "id": "…", "status": "sent" }- Only subscribed contacts can be texted (403 not_subscribed).
- Your brand name is added at the start and "Reply STOP to opt out." at the end, like every other text.
- Quiet hours (9 PM–8 AM in the person's time zone) return 409 quiet_hours with the time you can try again.
- Uses credits at the normal rate and counts toward the 25,000-credit daily limit for automated and API texts (429 daily_limit).
- Content our Acceptable Use Policy doesn't allow is refused (422 content_not_allowed). Your number must be approved to send.
GET/campaigns
Your 50 most recent campaigns with results.
Access: campaigns:read
Response
{ "campaigns": [ { "id": "…", "name": "Weekend sale", "status": "sent", "sent_at": "…",
"recipients": 1200, "sent": 1200, "delivered": 1184, "failed": 16,
"clicks": 212, "replies": 9, "opt_outs": 4 } ] }Errors
Errors return { "error": "code", "message": "What happened" }.
- 400
- invalid_body / invalid_json / invalid_phone — fix the request and try again.
- 401
- missing_key / invalid_key — the key is missing, wrong or revoked.
- 402
- insufficient_credits — add credits in Billing.
- 403
- missing_scope / not_subscribed — the key can't do this, or the contact isn't subscribed.
- 404
- not_found — no contact with that number.
- 409
- opted_out / quiet_hours / on_hold / cannot_send — see the message for details.
- 413
- body_too_large — request bodies can be up to 64 KB.
- 422
- consent_required / age_confirmation_required / content_not_allowed / not_mobile.
- 429
- rate_limited (over 120 requests a minute; see Retry-After) or daily_limit.
- 503
- provider_unavailable — temporary; no credits were used. Retry later.
Webhooks
Add an HTTPS URL under API & integrations → Webhooks and choose events. We POST JSON to it and expect any 2xx response within 5 seconds. Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. After 50 failures in a row the webhook is turned off and you can turn it back on.
contact.subscribed— New subscriber: Someone joins your list (any signup page, form, keyword, import, API or Shopify).contact.opted_out— Opt-out: A subscriber texts STOP or is opted out by your team, the API or Shopify.message.received— Incoming reply: A contact texts your number.message.status— Delivery status: A text you sent is delivered or fails.
POST https://your-site.example/encore-events
X-Encore-Signature: t=1793023331,v1=5f2b…
X-Encore-Event: contact.subscribed
X-Encore-Delivery: 8c1d…
{ "id": "8c1d…", "event": "contact.subscribed", "created_at": "2026-10-25T14:02:11Z",
"workspace_id": "…",
"data": { "contact_id": "…", "phone": "+15551234567", "name": "Sam Lee", "email": null,
"tags": [], "source": "form", "subscribed_at": "2026-10-25T14:02:11Z" } }Check the signature
Each webhook has its own signing secret (shown in the app). The X-Encore-Signature header holds a timestamp and an HMAC-SHA256 of <timestamp>.<raw body>. Reject requests that don't match or are more than 5 minutes old. Use the delivery id to ignore repeats.
// Node.js — verify an Encore Text webhook
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const [, t, v1] = /^t=(\d+),v1=([a-f0-9]{64})$/.exec(header ?? "") ?? [];
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5-minute window
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
return timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Zapier
- Trigger a Zap from Encore Text: use the "Webhooks by Zapier → Catch Hook" trigger, paste its URL as a webhook in Encore Text, then click "Send test event".
- Add contacts or send texts from a Zap: use "Webhooks by Zapier → Custom Request" (POST) to an endpoint above with the header
Authorization: Bearer etk_…and a JSON body.
Shopify
Connect a store under API & integrations → Shopify. You'll get a private URL to add as a webhook in Shopify (Settings → Notifications → Webhooks) for customer creation, customer update and order creation, plus a place to paste Shopify's signing key. We only add customers whose Shopify SMS marketing consent is "subscribed", and a Shopify SMS unsubscribe opts them out. No Shopify app install is needed.
Start an automation from your system
Automations with the "Webhook / API event" trigger have their own private URL and key (shown in the automation's trigger settings). Send the key as Authorization: Bearer … and POST { "phone": "+15551234567" } to it to start that automation for a subscribed contact.
Questions? Email support@encoretext.com. Use of the API is covered by our Terms of Service and Acceptable Use Policy.