Skip to content
SecureThreadDevelopers

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.

Send a message from a published templateShell
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.
Check that a key worksShell
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

Checking the status of a message

What the owner needs

Sent-mail history (own)

templates

What it opens

Listing the published templates a key may use

What the owner needs

Template use (shared)

contacts

What it opens

Listing, creating and updating contacts

What the owner needs

Manage own contacts

send

What it opens

Sending a message and listing the mailboxes it can use

What the owner needs

Send email, and use of the shared templates a send names

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.
Send one messageHTTP
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.
Check a messageShell
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

The provider accepted a message. It is not confirmation that anyone has it.

message.failed

When it happens

A message could not be sent.

message.retrying

When it happens

A send failed for a passing reason and will be tried again.

message.needs_review

When it happens

The outcome of a send is unclear and a person should check it.

message.recipients_refused

When it happens

The provider refused some of a message’s recipients.

message.bounce_reported

When it happens

After accepting a message, the provider reported a bounce.

message.complaint_reported

When it happens

The provider reported that a recipient marked a message as spam.

message.delay_reported

When it happens

The provider reported that a message is delayed.

approval.requested

When it happens

Someone asked for approval to send.

approval.decided

When it happens

An approval request was approved or rejected.

campaign.completed

When it happens

A campaign finished sending.

campaign.paused

When it happens

A campaign was paused.

schedule.run_failed

When it happens

A scheduled or recurring send could not run.

mailbox.needs_reconnect

When it happens

A connected mailbox’s sign-in stopped working.

assistant.send_requested

When it happens

An AI assistant asked to send a message.

webhook.test

When it happens

Sent only by Send test. It reports nothing that happened.
Verify a call (Node.js)JavaScript
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.
Add the server to Claude CodeShell
claude mcp add --transport http securethread https://app.securethread.co/mcp

07

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

VALIDATION_FAILED

What it means

A field is missing or wrong, an unknown field was sent, or the Idempotency-Key header is not 8 to 128 visible characters.

401

Code

UNAUTHENTICATED

What it means

The key is missing, unknown, revoked or expired, or its owner is no longer active.

403

Code

FORBIDDEN

What it means

The key lacks the scope, the address is not allowed, or the owner lost the access the scope needs.

404

Code

NOT_FOUND

What it means

The id does not exist, belongs to someone else, or is in another workspace.

409

Code

CONFLICT

What it means

The template has no published version, or the request conflicts with the current state.

409

Code

IDEMPOTENCY_IN_PROGRESS

What it means

A send with the same key is still being handled. Wait for the Retry-After seconds, then retry with the same key.

422

Code

IDEMPOTENCY_KEY_REUSED

What it means

The key was used before for a different request. Use a new key only for new content.

422

Code

CONTENT_REJECTED

What it means

The template content cannot be sent as it stands.

422

Code

SENDER_NOT_ALLOWED

What it means

The mailbox cannot send for this owner.

429

Code

RATE_LIMITED

What it means

The key has used its budget for this kind of call. Wait for the Retry-After seconds.

429

Code

QUOTA_EXCEEDED

What it means

The plan’s or the organization’s sending limit is reached. This is not a short wait.

503

Code

MAINTENANCE

What it means

SecureThread is paused for maintenance. Lookups still work, and sends wait.
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.

Send from the mailboxes you already useA person approves what needs approvalCredentials encrypted with AES-256-GCMEvery action in an append-only audit logAI assistants governed over MCP1,499 starter templatesHonest status: accepted is never called delivered