Developers
Build on SecureThread
Connect your own tools to SecureThread with a REST API and workspace keys, signed event webhooks, an MCP server for AI assistants, or no-code recipes for Zapier, Make and n8n.
Scoped workspace keys
Each key acts as the person who created it, with only the scopes it was given. Keys can expire and can be limited to address ranges.
Signed event webhooks
SecureThread calls your HTTPS endpoint when a message, approval, campaign, schedule, mailbox or AI assistant event happens. Every call is signed so you can verify it.
Retry-safe sends
Send one message from a published template with an Idempotency-Key header. A retry with the same key returns the first answer, so the message is not sent twice.
Clear message status
A message reports one of six statuses: queued, sending, accepted, retrying, failed or cancelled. Accepted means the mail server took the message. It does not prove that a person has it.
MCP server for AI assistants
Claude, ChatGPT, Cursor and VS Code can look things up and prepare email as one signed-in employee. Each email an assistant asks to send waits for a person to approve it, unless your organization turns on automatic sending for narrow, low-risk emails.
Recipes for Zapier, Make and n8n
Step-by-step recipes connect those tools through event webhooks and the public API. There is no native app for them yet.
Try it
Send from your own code
One request sends a published template through a connected mailbox, with every check and approval the app applies. Pick your language.
curl -X POST https://app.securethread.co/api/v1/public/messages \ -H "Authorization: Bearer $SECURETHREAD_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: onboarding-casey-0001" \ -d '{ "templateId": "<template-id>", "connectionId": "<mailbox-id>", "to": [{ "email": "[email protected]", "name": "Casey Example" }], "variables": { "first_name": "Casey" } }'01
Workspace API keys
The public API accepts one kind of credential: a workspace API key, sent as a bearer token. A person with the Manage settings permission creates the key under Administration › API keys. The secret is shown once, and SecureThread keeps only its hash, so store it the way you store a password. Cookies and sessions are never accepted on these routes.
- Keys start with stk_. A key in the query string is never accepted, because it would reach logs.
- A key acts as the person who created it, its owner. It can do only what the owner may do, within its scopes.
- Administrative powers never pass to a key, so a key made by an administrator still cannot change settings.
- A key works only inside the workspace it was created in. Ids from another workspace return a not-found error.
- Keys can expire after 30, 90 or 365 days, or never. They can also be limited to address ranges, such as 203.0.113.0/24.
- A revoked or expired key stops working on its next call. Deactivating the owner’s account stops every key they created.
- Check a new key first. A working key answers with its workspace, owner and scopes.
curl https://app.securethread.co/api/v1/public/whoami -H "Authorization: Bearer <your-api-key>"02
Scopes
Each key carries one or more scopes. A route refuses a key that lacks its scope with a forbidden error, whatever the owner may do. Give each key only the scopes its automation needs.
read
What it opens
What the owner needs
templates
What it opens
What the owner needs
contacts
What it opens
What the owner needs
send
What it opens
What the owner needs
03
Send from a published template
A send names a published template, a mailbox the owner may send from, and one to 50 recipients. The subject, preview text and body come from the template, and the request cannot set them. Variables fill in the values the template asks for.
- Find a template with the templates scope. The list returns metadata and variables only, never the subject, preview text or body.
- Find a mailbox with the send scope. Its id is the connection id that a send names. Mailboxes that are signed out or suspended are left out.
- Keep contacts with the contacts routes. Creating a contact for an address that already exists updates it, and addresses match without regard to case. Contacts made through the API are personal to the owner.
- Send one Idempotency-Key header with every send: 8 to 128 visible characters, such as a UUID. A retry with the same key and the same request returns the first answer, so the message is not sent twice.
- A send answers 201 with status QUEUED when the message is with the sending queue. It answers 202 with status PENDING_APPROVAL when an administrator must approve it first. Nothing is queued until a different administrator approves it.
- Every send gate that applies in the web app applies here too: the mailbox’s access, sending domains, the sandbox, approvals, the content policy, plan limits and rate limits.
POST https://app.securethread.co/api/v1/public/messagesAuthorization: Bearer <your-api-key>Content-Type: application/jsonIdempotency-Key: <unique-value-per-message> { "templateId": "<published-template-id>", "connectionId": "<mailbox-id>", "to": [{ "email": "[email protected]", "name": "Alex Example" }], "variables": { "first_name": "Alex", "interview_date": "Monday 3 November" }}04
Check what happened to a send
Every send gets a message id. Use the read scope to look up that message and its status. Accepted means the mail server accepted the message. It does not show that the recipient has it, and SecureThread does not report what happens after that.
- The status is one of queued, sending, accepted, retrying, failed or cancelled.
- The accepted time records when the mail server accepted the message.
- The failure reason is plain English, never the provider’s own text. It appears while a send is failing or being tried again.
- When the outcome is not known, the reason says the provider may have accepted the message. Check with the recipient before sending it again.
- A message that a mailbox provider has already accepted cannot be recalled.
curl https://app.securethread.co/api/v1/public/messages/<message-id> -H "Authorization: Bearer <your-api-key>"05
Signed event webhooks
Subscribe an HTTPS endpoint to the events you choose, and SecureThread calls it when they happen, so you do not have to poll for changes. An administrator adds endpoints under Administration › Webhooks, and your plan must include webhooks. Payloads carry identifiers only, never message content or addresses.
- The endpoint must use https on the standard port, with a domain name rather than an IP address. The name must resolve only to public addresses.
- Up to 10 endpoints per workspace. Events cover the whole workspace, so an endpoint gets them for every member’s messages, not only its creator’s.
- Each call carries a webhook-id, a webhook-timestamp and a webhook-signature header, in the Standard Webhooks scheme. The signing secret starts with whsec_ and is shown once.
- Verify the signature over the raw body, exactly as it arrived. Re-serializing the JSON changes the bytes, and the signature covers them.
- Treat webhook-id as an idempotency key. A call can arrive more than once.
- A 2xx answer within 10 seconds counts as success. Other answers are tried again over about two days, eight attempts in all. A 410 Gone answer switches the endpoint off at once.
- After five calls in a row fail every attempt, SecureThread switches the endpoint off and writes the reason to the audit log. Calls are listed for 30 days, and a finished call can be replayed.
message.accepted
When it happens
message.failed
When it happens
message.retrying
When it happens
message.needs_review
When it happens
message.recipients_refused
When it happens
message.bounce_reported
When it happens
message.complaint_reported
When it happens
message.delay_reported
When it happens
approval.requested
When it happens
approval.decided
When it happens
campaign.completed
When it happens
campaign.paused
When it happens
schedule.run_failed
When it happens
mailbox.needs_reconnect
When it happens
assistant.send_requested
When it happens
webhook.test
When it happens
import { createHmac, timingSafeEqual } from 'node:crypto' function verify(secret, headers, rawBody) { const id = headers['webhook-id'] const timestamp = Number(headers['webhook-timestamp']) if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false const key = Buffer.from(secret.slice('whsec_'.length), 'base64') const expected = `v1,${createHmac('sha256', key).update(`${id}.${timestamp}.${rawBody}`).digest('base64')}` return headers['webhook-signature'] .split(' ') .some( (candidate) => candidate.length === expected.length && timingSafeEqual(Buffer.from(candidate), Buffer.from(expected)), )}06
MCP server for AI assistants
SecureThread runs a Model Context Protocol server. An assistant such as Claude, ChatGPT, Cursor or VS Code can look things up in SecureThread and prepare email. It acts as one signed-in employee, and only within that person’s permissions at the moment it acts. Assistants sign in with OAuth 2.1 and PKCE, over Streamable HTTP.
- An administrator turns AI assistant access on under Administration › AI access. It is off by default for every organization.
- Each person approves each connection in their own browser, and chooses the access levels that connection gets.
- Access levels, such as View, Write drafts and Send email, limit what a connection may do.
- No assistant can approve or reject anything, publish a shared template, invite people, change roles or change organization settings.
- A personal access token lets a client that cannot open a browser act as you. Use one only for a client you run on your own computer.
- The server keeps no session between requests and sends no messages on its own.
- In Claude Code, run /mcp, choose securethread and then Authenticate. Cursor and VS Code take the same endpoint address in their MCP configuration files.
claude mcp add --transport http securethread https://app.securethread.co/mcp07
Zapier, Make and n8n recipes
The recipes connect Zapier, Make and n8n to SecureThread with event webhooks and the public API. There is no native app for these tools, and SecureThread does not change CRM records; that side lives in your tool. Menu names change between versions, so check each step against your own tool.
- Act when a send fails: a message.failed call starts the recipe, which then looks up that message for its failure reason.
- Send a template email from a CRM trigger: the tool’s own trigger starts the recipe, which can keep the recipient’s contact up to date and then send from a published template.
- Build the Idempotency-Key from ids that come from the trigger. A key made from a random value on each run does not protect a re-run.
- Filter webhook events in your tool. Events cover the whole workspace, and a message.failed call carries only the message id.
- Create the API key and the webhook endpoint in the web app. A key cannot create keys or endpoints.
- If a tool cannot see the raw body, run your own small HTTPS endpoint. It checks the signature and forwards verified calls to the tool. SecureThread does not run that endpoint for you.
08
Errors and limits
Every error has the same body: an error object with a code, a message and details, plus a request id and a timestamp. Quote the request id when you ask for help. Limits are counted per API key over a rolling five-minute window.
- Each key has a shared budget of 600 calls across every public route. Templates, contacts and mailboxes have 120 calls each, sends have 60, and message status checks have 300.
- A send also counts against the owner’s own budget of 120 calls, which the web app’s sends share.
- A call over a budget answers 429 with a Retry-After header, in seconds.
- A workspace may hold 25 active keys. A message can name up to 50 recipients and up to 100 variables.
- Idempotency keys are remembered for 24 hours and belong to the key that sent them.
400
Code
What it means
401
Code
What it means
403
Code
What it means
404
Code
What it means
409
Code
What it means
409
Code
What it means
422
Code
What it means
422
Code
What it means
422
Code
What it means
429
Code
What it means
429
Code
What it means
503
Code
What it means
FAQ
Questions, answered
Do we need a particular plan for webhooks?
Yes. Event webhooks need a plan that includes webhooks. SecureThread refuses to create or switch on an endpoint, and to send a test call, when the plan does not include them.
Can an API key create other keys or webhook endpoints?
No. Creating a key or an endpoint needs a signed-in person with the Manage settings permission, in the web app, under Administration › API keys or Administration › Webhooks.
Does an accepted message mean the recipient has it?
No. Accepted means the mail server took responsibility for the message. SecureThread never reports that a person has it, so treat accepted as one step, not the final result.
What happens if an automation retries a send?
With an Idempotency-Key header, the same request with the same key returns the first answer, and the message is not sent twice. Without the header, a retry sends the message again.
Can an AI assistant send email without approval?
Only if your organization has turned on automatic sending for narrow, low-risk emails. Otherwise a person approves each email an assistant asks to send, and assistants cannot approve anything themselves.
Build on SecureThread
Create a workspace key, send from a published template, and get signed events back. Every API send passes the same checks and approvals as one from the app.