Postilio docs

API reference

Base URL https://api.postilio.eu. Every request needs an API key: Authorization: Bearer pk_live_… (or pk_test_…), see Authentication. Errors are described in Errors and limits.

This page is generated from the OpenAPI document, which you can also download for a client generator.

Emails

Send an email

POST /v1/emails

Needs the emails:send scope. Stores one message per recipient and answers 202 with their ids, in the order of to. A recipient on the suppression list is accepted but not sent, and listed in suppressed. The sender's domain must be verified. A test key (pk_test_) never sends: its messages get simulated events. In the sandbox, live mail goes only to the organization's members and verified domains (422 sandbox_recipient_not_allowed), within a daily and a per-minute limit (429 with Retry-After).

Parameters

NameInTypeRequiredDescription
Idempotency-Keyheaderstringno1 to 256 characters. A repeat within 24 hours answers as the first request did, without sending again.

Request body

SendEmailRequest

FieldTypeRequiredDescription
fromstringyesAn address on a verified domain of the key's project, optionally with a display name (without '@' or ',').
toarray of stringyes1 to 50 bare addresses, without display names; each gets a message of its own.
subjectstringyesAt most 998 characters, without control characters.
textstringnoThe plain-text body; text, html or both.
htmlstringnoThe HTML body; text, html or both.
tagstringnoOptional; at most 64 letters, digits, '-' or '_'.
replyTostringnoOptional; one address, with or without a display name.
attachmentsarray of EmailAttachmentnoOptional; one with a contentId is inline, for cid: references in the HTML.

Responses

StatusMeaningBody
202AcceptedSendEmailResponse
400Bad RequestHttpValidationProblemDetails
422Unprocessable EntityErrorResponse
409ConflictErrorResponse
429Too Many RequestsErrorResponse

Get an email and its events

GET /v1/emails/{id}

Needs the emails:read scope. A test key finds only test messages and a live key only live ones; the other mode's are 404.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
200OKEmailDetails
404Not Found—

Domains

Add a sending domain

POST /v1/domains

Needs the domains:manage scope. Answers the domain as pending, with the two CNAME records to create in its DNS. postilio.eu and names under it cannot be added. 409 domain_exists when the project has it already.

Request body

CreateDomainRequest

FieldTypeRequiredDescription
namestringyesA host name of at least two labels, such as mail.example.com; not an IP address, not under postilio.eu.

Responses

StatusMeaningBody
201CreatedDomainResponse
400Bad RequestHttpValidationProblemDetails
409ConflictErrorResponse

List sending domains

GET /v1/domains

Needs the domains:manage scope.

Responses

StatusMeaningBody
200OKDomainList

Get a sending domain

GET /v1/domains/{id}

Needs the domains:manage scope.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
200OKDomainResponse
404Not Found—

Remove a sending domain

DELETE /v1/domains/{id}

Needs the domains:manage scope. Mail from the domain stops at once.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
204No Content—
404Not Found—

Check a domain's DNS records now

POST /v1/domains/{id}/check

Needs the domains:manage scope. Checks now instead of at the next periodic check (every 10 minutes) and answers the domain's new state. Once a minute per domain: 429 too_many_checks with Retry-After. 503 dns_unavailable when DNS gave no answer.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
200OKDomainResponse
404Not Found—
429Too Many RequestsErrorResponse
503Service UnavailableErrorResponse

Suppressions

List suppressed addresses

GET /v1/suppressions

Needs the suppressions:manage scope. Newest first; pass next as before for the next page.

Parameters

NameInTypeRequiredDescription
qquerystringnoPart of an address.
reasonquerystringnohard_bounce, complaint or manual.
beforequerystring (uuid)noThe next of the previous page.
limitqueryinteger (int32)no1 to 100; 50 when left out.

Responses

StatusMeaningBody
200OKSuppressionList
400Bad RequestHttpValidationProblemDetails

Suppress an address

POST /v1/suppressions

Needs the suppressions:manage scope. Adds the address with reason manual. 409 address_already_suppressed when it is on the list already.

Request body

CreateSuppressionRequest

FieldTypeRequiredDescription
addressstringyesOne bare address, at most 254 characters.

Responses

StatusMeaningBody
201CreatedSuppressionResponse
400Bad RequestHttpValidationProblemDetails
409ConflictErrorResponse

Remove a suppressed address

DELETE /v1/suppressions/{id}

Needs the suppressions:manage scope. A complaint is removed only with a reason, {"reason": "…"} of 10 to 500 characters; without one the answer is 422 reason_required. A hard bounce or manual entry needs no body.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Request body (optional)

RemoveSuppressionRequest

FieldTypeRequiredDescription
reasonstringnoRequired, at least 10 characters, when the entry is a complaint; kept with the removal.

Responses

StatusMeaningBody
204No Content—
404Not Found—
422Unprocessable EntityErrorResponse
400Bad RequestHttpValidationProblemDetails

Webhooks

List webhook endpoints

GET /v1/webhooks

Needs the webhooks:manage scope.

Responses

StatusMeaningBody
200OKWebhookEndpointList

Add a webhook endpoint

POST /v1/webhooks

Needs the webhooks:manage scope. The answer holds the signing secret, the only time it is shown. At most 10 endpoints per project: 409 webhook_endpoint_limit_reached.

Request body

CreateWebhookEndpointRequest

FieldTypeRequiredDescription
urlstringyesAn absolute https:// URL on a public address, at most 2048 characters, without credentials or a fragment.
eventsarray of stringyesOne or more of: accepted, queued, delivered, deferred, bounced, expired, complained, suppressed.
descriptionstringnoOptional; at most 200 characters.
modestringnolive (the default) or test.

Responses

StatusMeaningBody
201CreatedCreatedWebhookEndpoint
400Bad RequestHttpValidationProblemDetails
409ConflictErrorResponse

Get a webhook endpoint

GET /v1/webhooks/{id}

Needs the webhooks:manage scope.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
200OKWebhookEndpointResponse
404Not Found—

Change a webhook endpoint

PATCH /v1/webhooks/{id}

Needs the webhooks:manage scope. Only the fields you send change. paused: false resumes a paused endpoint.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Request body

UpdateWebhookEndpointRequest

FieldTypeRequiredDescription
urlstringno
eventsarray of stringno
descriptionstringnoAn empty string removes it.
pausedbooleannotrue pauses the endpoint, false resumes it.

Responses

StatusMeaningBody
200OKWebhookEndpointResponse
400Bad RequestHttpValidationProblemDetails
404Not Found—

Remove a webhook endpoint

DELETE /v1/webhooks/{id}

Needs the webhooks:manage scope.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
204No Content—
404Not Found—

Rotate the signing secret

POST /v1/webhooks/{id}/secret

Needs the webhooks:manage scope. Answers the new secret once. The old one keeps signing as well for 24 hours.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
200OKRotatedWebhookSecret
404Not Found—

Send a test event

POST /v1/webhooks/{id}/test

Needs the webhooks:manage scope. One webhook.test.v1 delivery, tried once without retries, also to a paused endpoint.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes

Responses

StatusMeaningBody
202AcceptedWebhookDeliveryResponse
404Not Found—

List an endpoint's deliveries

GET /v1/webhooks/{id}/deliveries

Needs the webhooks:manage scope. Newest first; pass next as before for the next page.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes
statusquerystringnopending, delivered or failed.
beforequerystring (uuid)noThe next of the previous page.
limitqueryinteger (int32)no1 to 100; 50 when left out.

Responses

StatusMeaningBody
200OKWebhookDeliveryList
400Bad RequestHttpValidationProblemDetails
404Not Found—

Retry a delivery

POST /v1/webhooks/{id}/deliveries/{deliveryId}/retry

Needs the webhooks:manage scope. One more attempt now, with the same id and payload. 409 delivery_still_pending while it is still being retried, endpoint_paused while the endpoint is paused.

Parameters

NameInTypeRequiredDescription
idpathstring (uuid)yes
deliveryIdpathstring (uuid)yes

Responses

StatusMeaningBody
202Accepted—
404Not Found—
409ConflictErrorResponse

Schemas

CreateDomainRequest

FieldTypeRequiredDescription
namestringyesA host name of at least two labels, such as mail.example.com; not an IP address, not under postilio.eu.

CreatedWebhookEndpoint

FieldTypeRequiredDescription
endpointWebhookEndpointResponseyes
secretstringyesThe signing secret (whsec_…). Shown once: it is stored encrypted and never returned again.

CreateSuppressionRequest

FieldTypeRequiredDescription
addressstringyesOne bare address, at most 254 characters.

CreateWebhookEndpointRequest

FieldTypeRequiredDescription
urlstringyesAn absolute https:// URL on a public address, at most 2048 characters, without credentials or a fragment.
eventsarray of stringyesOne or more of: accepted, queued, delivered, deferred, bounced, expired, complained, suppressed.
descriptionstringnoOptional; at most 200 characters.
modestringnolive (the default) or test.

DnsRecord

FieldTypeRequiredDescription
typestringyes
namestringyes
valuestringyes
statusstringyesfound, missing, or unknown until the first check.

DomainList

A list in an object, so paging can be added without breaking clients.

FieldTypeRequiredDescription
dataarray of DomainResponseyes

DomainResponse

FieldTypeRequiredDescription
idstring (uuid)yes
namestringyes
statusstringyespending, verified or failing.
checkedAtstring (date-time) or nullyes
recordsarray of DnsRecordyes
createdAtstring (date-time)yes
addedBystring or nullyesThe name of the dashboard user who added it; null when it was added with an API key.
failingSincestring (date-time) or nullyesSince when its records are missing, while it is failing.
sent30dinteger (int32)yesLive messages sent from it today and the 29 days before (UTC); test and suppressed ones not.

EmailAttachment

FieldTypeRequiredDescription
fileNamestringyes
contentTypestringyesA MIME type such as application/pdf, optionally with parameters.
contentstringyesThe file, base64-encoded.
contentIdstringnoMakes the attachment inline; the HTML refers to it as cid: plus this value.

EmailDetails

FieldTypeRequiredDescription
idstring (uuid)yes
statusstringyes
fromstringyes
tostringyes
subjectstring or nullyes
tagstring or nullyes
acceptedAtstring (date-time)yes
testbooleanyes
eventsarray of EmailEventyes
viastringyesapi or smtp: how the message was submitted.

EmailEvent

FieldTypeRequiredDescription
typestringyes
occurredAtstring (date-time)yes
smtpCodeinteger (int16) or nullyes
responsestring or nullyes
attemptinteger (int16) or nullno
remoteHoststring or nullno
enhancedCodestring or nullno
classificationstring or nullno

ErrorResponse

FieldTypeRequiredDescription
errorstringyes

HttpValidationProblemDetails

FieldTypeRequiredDescription
typestring or nullno
titlestring or nullno
statusinteger (int32) or nullno
detailstring or nullno
instancestring or nullno
errorsmap of array of stringno

RemoveSuppressionRequest

FieldTypeRequiredDescription
reasonstringnoRequired, at least 10 characters, when the entry is a complaint; kept with the removal.

RotatedWebhookSecret

FieldTypeRequiredDescription
secretstringyesThe new signing secret, shown once.
previousSecretExpiresAtstring (date-time)yesUntil then deliveries are signed with the old secret as well.

SendEmailRequest

FieldTypeRequiredDescription
fromstringyesAn address on a verified domain of the key's project, optionally with a display name (without '@' or ',').
toarray of stringyes1 to 50 bare addresses, without display names; each gets a message of its own.
subjectstringyesAt most 998 characters, without control characters.
textstringnoThe plain-text body; text, html or both.
htmlstringnoThe HTML body; text, html or both.
tagstringnoOptional; at most 64 letters, digits, '-' or '_'.
replyTostringnoOptional; one address, with or without a display name.
attachmentsarray of EmailAttachmentnoOptional; one with a contentId is inline, for cid: references in the HTML.

SendEmailResponse

FieldTypeRequiredDescription
idsarray of string (uuid)yesOne message per recipient, in the order of to.
suppressedarray of stringyesRecipients on the project's suppression list: accepted with status suppressed, not sent.

SuppressionList

FieldTypeRequiredDescription
dataarray of SuppressionResponseyes
nextstring (uuid) or nullyesPass as before for the next page; null on the last page.

SuppressionResponse

FieldTypeRequiredDescription
idstring (uuid)yes
addressstringyes
reasonstringyeshard_bounce, complaint or manual.
detailstring or nullyesThe remote server's answer for a bounce, or who added it.
sourceMessageIdstring (uuid) or nullyesThe message that caused it; it may already be gone from the log.
createdAtstring (date-time)yes

UpdateWebhookEndpointRequest

Every field is optional; what is left out stays as it is.

FieldTypeRequiredDescription
urlstringno
eventsarray of stringno
descriptionstringnoAn empty string removes it.
pausedbooleannotrue pauses the endpoint, false resumes it.

WebhookDeliveryList

FieldTypeRequiredDescription
dataarray of WebhookDeliveryResponseyes
nextstring (uuid) or nullyesPass as before for the next page; null on the last page.

WebhookDeliveryResponse

FieldTypeRequiredDescription
idstring (uuid)yes
eventIdstring (uuid)yesThe webhook-id header: the same for every attempt.
typestringyesThe payload type, e.g. email.delivered.v1.
emailIdstring (uuid) or nullyes
tostring or nullyes
statusstringyespending, delivered or failed.
attemptsinteger (int32)yes
lastAttemptAtstring (date-time) or nullyes
lastStatusCodeinteger (int32) or nullyes
lastErrorstring or nullyes
lastDurationMsinteger (int32) or nullyes
responseSnippetstring or nullyesThe start of the last response body, at most 256 characters.
nextAttemptAtstring (date-time) or nullyesSet while pending.
createdAtstring (date-time)yes

WebhookEndpointList

FieldTypeRequiredDescription
dataarray of WebhookEndpointResponseyes

WebhookEndpointResponse

FieldTypeRequiredDescription
idstring (uuid)yes
urlstringyes
descriptionstring or nullyes
eventsarray of stringyesThe message events it gets: accepted, queued, delivered, deferred, bounced, expired, complained, suppressed.
modestringyeslive, or test: a test endpoint gets the events of test-key messages only.
pausedbooleanyes
pauseReasonstring or nullyes
pausedAtstring (date-time) or nullyes
failingSincestring (date-time) or nullyesEvery attempt since then failed; after 72 hours of that the endpoint is paused.
secretHintstringyesThe last characters of the signing secret.
previousSecretExpiresAtstring (date-time) or nullyesUntil then the secret from before the last rotation still signs as well.
createdAtstring (date-time)yes
lastDeliveryanyyes

WebhookLastDelivery

FieldTypeRequiredDescription
atstring (date-time)yesWhen the attempt was made.
statusCodeinteger (int32) or nullyesThe receiver's HTTP status; null when no answer came (timeout, refused, blocked address).
statusstringyesThe delivery's status now: pending (to be retried), delivered or failed.