Emails
List Emails, the latest first
GET /v1/emails emails:read
Without bodies: GET /v1/emails/{id} has those.
Query
limitintegercursorstringthreadIdstringThe Emails of one Thread
Responses
- 200
A page of Emails
Fields
-
dataEmail[] requiredFields
-
idstring required -
threadIdstring | null required -
emailAddressIdstring | null requiredThe Email address it went from or came to
-
direction"inbound" | "outbound" required -
source"desk" | "api" | "campaign" | "flow" | null requiredWho started it. An inbound Email has none
-
decisionstring requiredsent,received, or the refusal that stopped it (suppressed, …) -
status"pending" | "unknown" | "accepted" | "sent" | "delivered" | "bounced" | "complained" | "failed" | "refused" | "received" required -
fromstring | null required -
fromNamestring | null required -
tostring[] required -
ccstring[] required -
subjectstring | null required -
snippetstring | null requiredThe first 200 characters of the text. Null once retention has cleared it
-
sizeBytesinteger requiredThe raw message, attachments included
-
messageIdstring required -
inReplyTostring | null required -
referencesstring[] required -
leadIdstring | null requiredOnly when one Lead has the address
-
campaignIdstring | null required -
failureReason"hard_bounce" | "soft_bounce" | "invalid_recipient" | "provider_suppressed" | "rejected" | "unknown" | null required -
errorCodestring | null requiredThe provider's own code
-
createdAtstring required -
sentAtstring | null required -
deliveredAtstring | null required -
failedAtstring | null required -
complainedAtstring | null required -
receivedAtstring | null required -
autoReplyboolean requiredAn inbound automatic answer, such as an out-of-office: kept, and it doesn’t reopen a closed Thread
-
bodyClearedAtstring | null requiredWhen retention, or the Lead’s removal, cleared its bodies. The row stays
-
-
nextCursorstring | null required
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Send an Email
POST /v1/emails emails:write
We carry Emails through your own sending provider, and consent, content, the footer with your physical address, and your domain’s SPF, DKIM and DMARC are yours: see the Email guide. Every Email gets a one-click unsubscribe, and we refuse an address on your suppression list. Test mode never reaches a provider. A refused attempt still has its row, named by error.details.emailId. A 201 means we asked the provider: status says how that went, and unknown is never sent again automatically.
Headers
Idempotency-KeystringMakes the request safe to retry. For 24h, a request with the same key and body replays the stored response (with
Idempotent-Replayed: true); the same key with a different request is a 422idempotency_key_reused, and while the first request is still running a 409idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that saysHcc-Should-Retry: false, such as504 call_outcome_unknown.
Body
-
fromstring requiredThe id of one of your Email addresses
-
tostring | string[] requiredOne recipient: an API send has exactly one
to -
ccstring | string[] -
subjectstring required -
textstringThe plain-text part
-
htmlstringThe HTML part. We never make up a text part you didn’t write
-
replyTostringThe id of the Email this answers: it joins that Thread, with its threading headers
-
attachmentsobject[]Fields
-
filenamestring required -
contentTypestringDefault
"application/octet-stream". -
contentstring requiredBase64
-
-
idempotencyKeystringSending again with the same key answers the Email it already sent
Responses
- 201
The provider was asked
Fields
-
dataEmail requiredOne email, inbound or outbound, including a refused attempt: the audit row of that attempt. Its bodies are on
GET /v1/emails/{id}.Fields
-
idstring required -
threadIdstring | null required -
emailAddressIdstring | null requiredThe Email address it went from or came to
-
direction"inbound" | "outbound" required -
source"desk" | "api" | "campaign" | "flow" | null requiredWho started it. An inbound Email has none
-
decisionstring requiredsent,received, or the refusal that stopped it (suppressed, …) -
status"pending" | "unknown" | "accepted" | "sent" | "delivered" | "bounced" | "complained" | "failed" | "refused" | "received" required -
fromstring | null required -
fromNamestring | null required -
tostring[] required -
ccstring[] required -
subjectstring | null required -
snippetstring | null requiredThe first 200 characters of the text. Null once retention has cleared it
-
sizeBytesinteger requiredThe raw message, attachments included
-
messageIdstring required -
inReplyTostring | null required -
referencesstring[] required -
leadIdstring | null requiredOnly when one Lead has the address
-
campaignIdstring | null required -
failureReason"hard_bounce" | "soft_bounce" | "invalid_recipient" | "provider_suppressed" | "rejected" | "unknown" | null required -
errorCodestring | null requiredThe provider's own code
-
createdAtstring required -
sentAtstring | null required -
deliveredAtstring | null required -
failedAtstring | null required -
complainedAtstring | null required -
receivedAtstring | null required -
autoReplyboolean requiredAn inbound automatic answer, such as an out-of-office: kept, and it doesn’t reopen a closed Thread
-
bodyClearedAtstring | null requiredWhen retention, or the Lead’s removal, cleared its bodies. The row stays
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 402
A billing stop: outbound only. Unsubscribes keep working
- 404
address_not_found, orreplyTonames no Email - 409
idempotency_key_in_use: a request with this key is still running - 413
too_large: over 25 MiB - 422
suppressed,address_not_ready,provider_unavailableortoo_many_recipients - 429
rate_limited, or another limit named byerror.code
Get one Email, with its bodies
GET /v1/emails/{id} emails:read
Path
idstring required
Responses
- 200
The Email
Fields
-
dataEmailWithBodies requiredOne email, inbound or outbound, including a refused attempt: the audit row of that attempt. Its bodies are on
GET /v1/emails/{id}.
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Get one Email’s raw message
GET /v1/emails/{id}/raw emails:read
The .eml as it went or came, as message/rfc822. 404 once retention cleared it.
Path
idstring required
Responses
- 200
The raw message
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
List Email addresses, the newest first
GET /v1/email-addresses emails:read
Query
limitintegercursorstring
Responses
- 200
A page of Email addresses
Fields
-
dataEmailAddress[] requiredFields
-
idstring required -
addressstring required -
displayNamestring | null required -
providerstring requiredYour sending provider.
simulatorin test mode -
status"unverified" | "ready" | "failed" requiredreadysends. Read from your provider -
statusReasonstring | null required -
inboundAddressstring requiredForward this address’s mail here to receive replies
-
forwardVerifiedAtstring | null requiredWhen one of its own Emails last came back through the forward, which proves the forward works
-
forwardVerifiedEmailIdstring | null requiredThat Email
-
emailFlowstring | null requiredThe Email flow inbound Email runs, if set.
hcc deploy --livebinds it -
webhookobject requiredWhere your provider reports delivery, bounces and complaints. Null in test mode
-
createdAtstring required -
updatedAtstring required
-
-
nextCursorstring | null required
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Add an Email address
POST /v1/email-addresses emails:write
In test mode it’s ready at once, on the simulator. In live mode it’s on your own sending provider (resend), with credentials.apiKey: its status is what the provider says of the address’s domain, which you verify there. Add webhook.url as a webhook in your provider, then send its signing secret as credentials.webhookSecret with PATCH.
Headers
Idempotency-KeystringMakes the request safe to retry. For 24h, a request with the same key and body replays the stored response (with
Idempotent-Replayed: true); the same key with a different request is a 422idempotency_key_reused, and while the first request is still running a 409idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that saysHcc-Should-Retry: false, such as504 call_outcome_unknown.
Body
-
addressstring required -
displayNamestring | null -
providerstringYour sending provider, in live mode. Test mode is always the simulator
-
credentialsobjectYour provider’s key. Write-only: it’s never returned
Responses
- 201
Added
Fields
-
dataEmailAddress requiredAn address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.
Fields
-
idstring required -
addressstring required -
displayNamestring | null required -
providerstring requiredYour sending provider.
simulatorin test mode -
status"unverified" | "ready" | "failed" requiredreadysends. Read from your provider -
statusReasonstring | null required -
inboundAddressstring requiredForward this address’s mail here to receive replies
-
forwardVerifiedAtstring | null requiredWhen one of its own Emails last came back through the forward, which proves the forward works
-
forwardVerifiedEmailIdstring | null requiredThat Email
-
emailFlowstring | null requiredThe Email flow inbound Email runs, if set.
hcc deploy --livebinds it -
webhookobject requiredWhere your provider reports delivery, bounces and complaints. Null in test mode
-
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 409
This address is already one of yours
- 422
provider_unavailable: not a provider we support - 429
rate_limited, or another limit named byerror.code
Get one Email address
GET /v1/email-addresses/{id} emails:read
Path
idstring required
Responses
- 200
The Email address
Fields
-
dataEmailAddress requiredAn address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.
Fields
-
idstring required -
addressstring required -
displayNamestring | null required -
providerstring requiredYour sending provider.
simulatorin test mode -
status"unverified" | "ready" | "failed" requiredreadysends. Read from your provider -
statusReasonstring | null required -
inboundAddressstring requiredForward this address’s mail here to receive replies
-
forwardVerifiedAtstring | null requiredWhen one of its own Emails last came back through the forward, which proves the forward works
-
forwardVerifiedEmailIdstring | null requiredThat Email
-
emailFlowstring | null requiredThe Email flow inbound Email runs, if set.
hcc deploy --livebinds it -
webhookobject requiredWhere your provider reports delivery, bounces and complaints. Null in test mode
-
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Update an Email address
PATCH /v1/email-addresses/{id} emails:write
Path
idstring required
Body
-
displayNamestring | null -
credentialsobjectMerged into the ones it holds: send only what changes, such as
webhookSecret. Write-only. A newapiKeyreads the domain again -
emailFlowstring | nullThe Email flow inbound Email to this address runs. It must be deployed to this environment. Null clears it
Responses
- 200
Updated
Fields
-
dataEmailAddress requiredAn address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.
Fields
-
idstring required -
addressstring required -
displayNamestring | null required -
providerstring requiredYour sending provider.
simulatorin test mode -
status"unverified" | "ready" | "failed" requiredreadysends. Read from your provider -
statusReasonstring | null required -
inboundAddressstring requiredForward this address’s mail here to receive replies
-
forwardVerifiedAtstring | null requiredWhen one of its own Emails last came back through the forward, which proves the forward works
-
forwardVerifiedEmailIdstring | null requiredThat Email
-
emailFlowstring | null requiredThe Email flow inbound Email runs, if set.
hcc deploy --livebinds it -
webhookobject requiredWhere your provider reports delivery, bounces and complaints. Null in test mode
-
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Remove an Email address
DELETE /v1/email-addresses/{id} emails:write
Its Emails stay, as the record of what it sent. Its forward stops being read.
Path
idstring required
Responses
- 204
Removed
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Read the domain’s status at your provider again
POST /v1/email-addresses/{id}/check emails:write
Read-only at your provider: verifying the domain is yours to do there. It’s also read again each day.
Path
idstring required
Responses
- 200
As your provider says now
Fields
-
dataEmailAddress requiredAn address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.
Fields
-
idstring required -
addressstring required -
displayNamestring | null required -
providerstring requiredYour sending provider.
simulatorin test mode -
status"unverified" | "ready" | "failed" requiredreadysends. Read from your provider -
statusReasonstring | null required -
inboundAddressstring requiredForward this address’s mail here to receive replies
-
forwardVerifiedAtstring | null requiredWhen one of its own Emails last came back through the forward, which proves the forward works
-
forwardVerifiedEmailIdstring | null requiredThat Email
-
emailFlowstring | null requiredThe Email flow inbound Email runs, if set.
hcc deploy --livebinds it -
webhookobject requiredWhere your provider reports delivery, bounces and complaints. Null in test mode
-
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
List the suppressed addresses, the latest first
GET /v1/email-suppressions emails:read
Query
limitintegercursorstringaddressstringLook up one address
Responses
- 200
A page of suppressions
Fields
-
dataEmailSuppression[] requiredFields
-
addressstring required -
reason"unsubscribe" | "hard_bounce" | "complaint" | "manual" | "api" requiredunsubscribe(the recipient used the link),hard_bounce,complaint(marked as spam),manual(a person) orapi -
sourceEmailIdstring | null requiredThe Email that caused it, if one did
-
createdBystring | null requiredThe API key, user or Email that wrote it
-
notestring | null required -
createdAtstring required
-
-
nextCursorstring | null required
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Suppress an address
POST /v1/email-suppressions emails:write
Idempotent: an address already suppressed is returned as it is (200).
Headers
Idempotency-KeystringMakes the request safe to retry. For 24h, a request with the same key and body replays the stored response (with
Idempotent-Replayed: true); the same key with a different request is a 422idempotency_key_reused, and while the first request is still running a 409idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that saysHcc-Should-Retry: false, such as504 call_outcome_unknown.
Body
-
addressstring required -
notestring | null
Responses
- 200
Already suppressed
Fields
-
dataEmailSuppression requiredAn address this environment may not email. It never expires, and lifting it keeps the history.
Fields
-
addressstring required -
reason"unsubscribe" | "hard_bounce" | "complaint" | "manual" | "api" requiredunsubscribe(the recipient used the link),hard_bounce,complaint(marked as spam),manual(a person) orapi -
sourceEmailIdstring | null requiredThe Email that caused it, if one did
-
createdBystring | null requiredThe API key, user or Email that wrote it
-
notestring | null required -
createdAtstring required
-
-
- 201
Suppressed
Fields
-
dataEmailSuppression requiredAn address this environment may not email. It never expires, and lifting it keeps the history.
Fields
-
addressstring required -
reason"unsubscribe" | "hard_bounce" | "complaint" | "manual" | "api" requiredunsubscribe(the recipient used the link),hard_bounce,complaint(marked as spam),manual(a person) orapi -
sourceEmailIdstring | null requiredThe Email that caused it, if one did
-
createdBystring | null requiredThe API key, user or Email that wrote it
-
notestring | null required -
createdAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 409
idempotency_key_in_use: a request with this key is still running - 422
idempotency_key_reused: this key was used with a different request - 429
rate_limited, or another limit named byerror.code
Lift a suppression
DELETE /v1/email-suppressions/{address} emails:write
Idempotent: an address that isn’t suppressed also answers 204. The history keeps both rows. Lifting an unsubscribe is yours to justify: CAN-SPAM forbids mailing someone who opted out.
Path
addressstring required
Responses
- 204
Lifted
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
List Email flows
GET /v1/email-flows flows:read
Responses
- 200
The Email flows deployed
Fields
-
dataEmailFlow[] requiredFields
-
idstring required -
namestring required -
versionstring required -
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code
Register Email flows
PUT /v1/email-flows flows:write
What hcc deploy sends (mode: "deploy"). An Email flow is its name and version; the logic is in your code. Inbound Emails reach one bound to their address with PATCH /v1/email-addresses/{id}, or in test mode the only one deployed. hcc.serve sends mode: "report" on startup, which deploys nothing: it sets the flow endpoint and answers with what is deployed. A deploy never removes a flow, or unbinds an address.
Body
-
flowsobject[] requiredFields
-
namestring required -
versionstring required
-
-
mode"report" | "deploy"Default
"report". -
endpointUrlstring -
alsoTestbooleanDefault
false.
Responses
- 200
The registered Email flows
Fields
-
dataEmailFlow[] requiredFields
-
idstring required -
namestring required -
versionstring required -
createdAtstring required -
updatedAtstring required
-
-
- 400
Invalid request
- 401
Missing or invalid API key
- 404
Not found
- 429
rate_limited, or another limit named byerror.code