HCC Docs

Emails

List Emails, the latest first

GET /v1/emails emails:read

Without bodies: GET /v1/emails/{id} has those.

Query

  • limit integer
  • cursor string
  • threadId string

    The Emails of one Thread

Responses

  • 200

    A page of Emails

    Fields
    • data Email[] required
      Fields
      • id string required
      • threadId string | null required
      • emailAddressId string | null required

        The Email address it went from or came to

      • direction "inbound" | "outbound" required
      • source "desk" | "api" | "campaign" | "flow" | null required

        Who started it. An inbound Email has none

      • decision string required

        sent, received, or the refusal that stopped it (suppressed, …)

      • status "pending" | "unknown" | "accepted" | "sent" | "delivered" | "bounced" | "complained" | "failed" | "refused" | "received" required
      • from string | null required
      • fromName string | null required
      • to string[] required
      • cc string[] required
      • subject string | null required
      • snippet string | null required

        The first 200 characters of the text. Null once retention has cleared it

      • sizeBytes integer required

        The raw message, attachments included

      • messageId string required
      • inReplyTo string | null required
      • references string[] required
      • leadId string | null required

        Only when one Lead has the address

      • campaignId string | null required
      • failureReason "hard_bounce" | "soft_bounce" | "invalid_recipient" | "provider_suppressed" | "rejected" | "unknown" | null required
      • errorCode string | null required

        The provider's own code

      • createdAt string required
      • sentAt string | null required
      • deliveredAt string | null required
      • failedAt string | null required
      • complainedAt string | null required
      • receivedAt string | null required
      • autoReply boolean required

        An inbound automatic answer, such as an out-of-office: kept, and it doesn’t reopen a closed Thread

      • bodyClearedAt string | null required

        When retention, or the Lead’s removal, cleared its bodies. The row stays

    • nextCursor string | null required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.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-Key string

    Makes 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 422 idempotency_key_reused, and while the first request is still running a 409 idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that says Hcc-Should-Retry: false, such as 504 call_outcome_unknown.

Body

  • from string required

    The id of one of your Email addresses

  • to string | string[] required

    One recipient: an API send has exactly one to

  • cc string | string[]
  • subject string required
  • text string

    The plain-text part

  • html string

    The HTML part. We never make up a text part you didn’t write

  • replyTo string

    The id of the Email this answers: it joins that Thread, with its threading headers

  • attachments object[]
    Fields
    • filename string required
    • contentType string

      Default "application/octet-stream".

    • content string required

      Base64

  • idempotencyKey string

    Sending again with the same key answers the Email it already sent

Responses

  • 201

    The provider was asked

    Fields
    • data Email required

      One email, inbound or outbound, including a refused attempt: the audit row of that attempt. Its bodies are on GET /v1/emails/{id}.

      Fields
      • id string required
      • threadId string | null required
      • emailAddressId string | null required

        The Email address it went from or came to

      • direction "inbound" | "outbound" required
      • source "desk" | "api" | "campaign" | "flow" | null required

        Who started it. An inbound Email has none

      • decision string required

        sent, received, or the refusal that stopped it (suppressed, …)

      • status "pending" | "unknown" | "accepted" | "sent" | "delivered" | "bounced" | "complained" | "failed" | "refused" | "received" required
      • from string | null required
      • fromName string | null required
      • to string[] required
      • cc string[] required
      • subject string | null required
      • snippet string | null required

        The first 200 characters of the text. Null once retention has cleared it

      • sizeBytes integer required

        The raw message, attachments included

      • messageId string required
      • inReplyTo string | null required
      • references string[] required
      • leadId string | null required

        Only when one Lead has the address

      • campaignId string | null required
      • failureReason "hard_bounce" | "soft_bounce" | "invalid_recipient" | "provider_suppressed" | "rejected" | "unknown" | null required
      • errorCode string | null required

        The provider's own code

      • createdAt string required
      • sentAt string | null required
      • deliveredAt string | null required
      • failedAt string | null required
      • complainedAt string | null required
      • receivedAt string | null required
      • autoReply boolean required

        An inbound automatic answer, such as an out-of-office: kept, and it doesn’t reopen a closed Thread

      • bodyClearedAt string | null required

        When 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, or replyTo names 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_unavailable or too_many_recipients

  • 429

    rate_limited, or another limit named by error.code

Get one Email, with its bodies

GET /v1/emails/{id} emails:read

Path

  • id string required

Responses

  • 200

    The Email

    Fields
    • data EmailWithBodies required

      One 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 by error.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

  • id string 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 by error.code

List Email addresses, the newest first

GET /v1/email-addresses emails:read

Query

  • limit integer
  • cursor string

Responses

  • 200

    A page of Email addresses

    Fields
    • data EmailAddress[] required
      Fields
      • id string required
      • address string required
      • displayName string | null required
      • provider string required

        Your sending provider. simulator in test mode

      • status "unverified" | "ready" | "failed" required

        ready sends. Read from your provider

      • statusReason string | null required
      • inboundAddress string required

        Forward this address’s mail here to receive replies

      • forwardVerifiedAt string | null required

        When one of its own Emails last came back through the forward, which proves the forward works

      • forwardVerifiedEmailId string | null required

        That Email

      • emailFlow string | null required

        The Email flow inbound Email runs, if set. hcc deploy --live binds it

      • webhook object required

        Where your provider reports delivery, bounces and complaints. Null in test mode

      • createdAt string required
      • updatedAt string required
    • nextCursor string | null required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.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-Key string

    Makes 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 422 idempotency_key_reused, and while the first request is still running a 409 idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that says Hcc-Should-Retry: false, such as 504 call_outcome_unknown.

Body

  • address string required
  • displayName string | null
  • provider string

    Your sending provider, in live mode. Test mode is always the simulator

  • credentials object

    Your provider’s key. Write-only: it’s never returned

Responses

  • 201

    Added

    Fields
    • data EmailAddress required

      An address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.

      Fields
      • id string required
      • address string required
      • displayName string | null required
      • provider string required

        Your sending provider. simulator in test mode

      • status "unverified" | "ready" | "failed" required

        ready sends. Read from your provider

      • statusReason string | null required
      • inboundAddress string required

        Forward this address’s mail here to receive replies

      • forwardVerifiedAt string | null required

        When one of its own Emails last came back through the forward, which proves the forward works

      • forwardVerifiedEmailId string | null required

        That Email

      • emailFlow string | null required

        The Email flow inbound Email runs, if set. hcc deploy --live binds it

      • webhook object required

        Where your provider reports delivery, bounces and complaints. Null in test mode

      • createdAt string required
      • updatedAt string 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 by error.code

Get one Email address

GET /v1/email-addresses/{id} emails:read

Path

  • id string required

Responses

  • 200

    The Email address

    Fields
    • data EmailAddress required

      An address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.

      Fields
      • id string required
      • address string required
      • displayName string | null required
      • provider string required

        Your sending provider. simulator in test mode

      • status "unverified" | "ready" | "failed" required

        ready sends. Read from your provider

      • statusReason string | null required
      • inboundAddress string required

        Forward this address’s mail here to receive replies

      • forwardVerifiedAt string | null required

        When one of its own Emails last came back through the forward, which proves the forward works

      • forwardVerifiedEmailId string | null required

        That Email

      • emailFlow string | null required

        The Email flow inbound Email runs, if set. hcc deploy --live binds it

      • webhook object required

        Where your provider reports delivery, bounces and complaints. Null in test mode

      • createdAt string required
      • updatedAt string required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.code

Update an Email address

PATCH /v1/email-addresses/{id} emails:write

Path

  • id string required

Body

  • displayName string | null
  • credentials object

    Merged into the ones it holds: send only what changes, such as webhookSecret. Write-only. A new apiKey reads the domain again

  • emailFlow string | null

    The Email flow inbound Email to this address runs. It must be deployed to this environment. Null clears it

Responses

  • 200

    Updated

    Fields
    • data EmailAddress required

      An address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.

      Fields
      • id string required
      • address string required
      • displayName string | null required
      • provider string required

        Your sending provider. simulator in test mode

      • status "unverified" | "ready" | "failed" required

        ready sends. Read from your provider

      • statusReason string | null required
      • inboundAddress string required

        Forward this address’s mail here to receive replies

      • forwardVerifiedAt string | null required

        When one of its own Emails last came back through the forward, which proves the forward works

      • forwardVerifiedEmailId string | null required

        That Email

      • emailFlow string | null required

        The Email flow inbound Email runs, if set. hcc deploy --live binds it

      • webhook object required

        Where your provider reports delivery, bounces and complaints. Null in test mode

      • createdAt string required
      • updatedAt string required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.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

  • id string required

Responses

  • 204

    Removed

  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.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

  • id string required

Responses

  • 200

    As your provider says now

    Fields
    • data EmailAddress required

      An address on your own domain that Emails go out from, through your sending provider. Its credentials are never returned.

      Fields
      • id string required
      • address string required
      • displayName string | null required
      • provider string required

        Your sending provider. simulator in test mode

      • status "unverified" | "ready" | "failed" required

        ready sends. Read from your provider

      • statusReason string | null required
      • inboundAddress string required

        Forward this address’s mail here to receive replies

      • forwardVerifiedAt string | null required

        When one of its own Emails last came back through the forward, which proves the forward works

      • forwardVerifiedEmailId string | null required

        That Email

      • emailFlow string | null required

        The Email flow inbound Email runs, if set. hcc deploy --live binds it

      • webhook object required

        Where your provider reports delivery, bounces and complaints. Null in test mode

      • createdAt string required
      • updatedAt string required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.code

List the suppressed addresses, the latest first

GET /v1/email-suppressions emails:read

Query

  • limit integer
  • cursor string
  • address string

    Look up one address

Responses

  • 200

    A page of suppressions

    Fields
    • data EmailSuppression[] required
      Fields
      • address string required
      • reason "unsubscribe" | "hard_bounce" | "complaint" | "manual" | "api" required

        unsubscribe (the recipient used the link), hard_bounce, complaint (marked as spam), manual (a person) or api

      • sourceEmailId string | null required

        The Email that caused it, if one did

      • createdBy string | null required

        The API key, user or Email that wrote it

      • note string | null required
      • createdAt string required
    • nextCursor string | null required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.code

Suppress an address

POST /v1/email-suppressions emails:write

Idempotent: an address already suppressed is returned as it is (200).

Headers

  • Idempotency-Key string

    Makes 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 422 idempotency_key_reused, and while the first request is still running a 409 idempotency_key_in_use. Server errors, 409s and 429s are not stored, except one that says Hcc-Should-Retry: false, such as 504 call_outcome_unknown.

Body

  • address string required
  • note string | null

Responses

  • 200

    Already suppressed

    Fields
    • data EmailSuppression required

      An address this environment may not email. It never expires, and lifting it keeps the history.

      Fields
      • address string required
      • reason "unsubscribe" | "hard_bounce" | "complaint" | "manual" | "api" required

        unsubscribe (the recipient used the link), hard_bounce, complaint (marked as spam), manual (a person) or api

      • sourceEmailId string | null required

        The Email that caused it, if one did

      • createdBy string | null required

        The API key, user or Email that wrote it

      • note string | null required
      • createdAt string required
  • 201

    Suppressed

    Fields
    • data EmailSuppression required

      An address this environment may not email. It never expires, and lifting it keeps the history.

      Fields
      • address string required
      • reason "unsubscribe" | "hard_bounce" | "complaint" | "manual" | "api" required

        unsubscribe (the recipient used the link), hard_bounce, complaint (marked as spam), manual (a person) or api

      • sourceEmailId string | null required

        The Email that caused it, if one did

      • createdBy string | null required

        The API key, user or Email that wrote it

      • note string | null required
      • createdAt string 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 by error.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

  • address string required

Responses

  • 204

    Lifted

  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.code

List Email flows

GET /v1/email-flows flows:read

Responses

  • 200

    The Email flows deployed

    Fields
    • data EmailFlow[] required
      Fields
      • id string required
      • name string required
      • version string required
      • createdAt string required
      • updatedAt string required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.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

  • flows object[] required
    Fields
    • name string required
    • version string required
  • mode "report" | "deploy"

    Default "report".

  • endpointUrl string
  • alsoTest boolean

    Default false.

Responses

  • 200

    The registered Email flows

    Fields
    • data EmailFlow[] required
      Fields
      • id string required
      • name string required
      • version string required
      • createdAt string required
      • updatedAt string required
  • 400

    Invalid request

  • 401

    Missing or invalid API key

  • 404

    Not found

  • 429

    rate_limited, or another limit named by error.code