Write a reply now and send it later
const url = 'https://api.threadkite.app/v1/scheduled-messages';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"threadId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","channel":"email","subject":"example","text":"example","html":"example","cc":[{"address":"accounts@example.co.uk","name":"example"}],"bcc":[{"address":"accounts@example.co.uk","name":"example"}],"attachmentIds":["2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"],"channelAccountId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","mailboxConnectionId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","template":{"templateId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","variables":[]},"signature":true,"sendAt":"2026-04-15T12:00:00Z","cancelOnReply":true}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.threadkite.app/v1/scheduled-messages \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "threadId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "channel": "email", "subject": "example", "text": "example", "html": "example", "cc": [ { "address": "accounts@example.co.uk", "name": "example" } ], "bcc": [ { "address": "accounts@example.co.uk", "name": "example" } ], "attachmentIds": [ "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" ], "channelAccountId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "mailboxConnectionId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "template": { "templateId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "variables": [] }, "signature": true, "sendAt": "2026-04-15T12:00:00Z", "cancelOnReply": true }'Sent as you at sendAt (2 minutes to 60 days ahead), through the normal send, so consent and the channel are checked then. With cancelOnReply (the default) it is cancelled if the contact writes first. Needs a signed-in person.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
object
Example
accounts@example.co.ukobject
Example
accounts@example.co.ukFiles from POST /v1/attachments
object
From GET /v1/threads/{id}/reply-channels
Values for the template’s variables, in order
Example
[ "Sarah"]Email: add the signature when it is sent (on unless false)
When to send it: at least 2 minutes and at most 60 days from now
Cancel it if the contact writes before then
Responses
Section titled “Responses”object
object
How many files are attached
A WhatsApp template is used
Once sent
Why it wasn’t sent, when it failed
You can change, send now or cancel it
Example
{ "author": { "id": "usr_8f2c", "name": "James Patel" }, "channel": "email", "status": "pending", "failureReason": "They have opted out of texts.", "cancelledReason": "contact_replied"}object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}Missing or invalid API key
object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}The organisation is on the Free plan (code upgrade_required)
object
Stable machine-readable reason
The feature that needs Pro
Where to upgrade or pay, in the web app
Example
{ "statusCode": 402, "code": "upgrade_required", "message": "Connections are part of Pro. Upgrade to connect texts, calls and more.", "feature": "extraSeats", "upgradePath": "/settings/billing"}The key does not have the scope (or the person the permission)
object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}Rate limit reached
object
Stable machine-readable reason
Seconds until another request will be accepted
Example
{ "statusCode": 429, "code": "rate_limited", "message": "Too many requests. Each key can make 120 requests a minute; try again in 12 seconds.", "retryAfter": 12}