List what was delivered where, and how it went Run in API Explorer
Every time Commune handed one of this newsletter's events to one of its
destinations, newest first: which event, which destination, what came
back, and whether it was a first try or a retry. Needs sending: read.
This is the answer to "my endpoint never received that event". Each row
names an event_id, which is the same string the consumer sees in the
Commune-Event-Id header and in the envelope's id, so a line in your
own logs and a row here can be matched up. ?event_id= goes the other
way: give it an id and get every attempt at delivering that one event.
An event is delivered once per destination, so one event with three
destinations produces at least three rows here. ?destination_id=
narrows to one of them, and ?status=failed is the usual first read.
A retry is a new row, not an edit. attempt is 1 on the first try
and one higher on each retry, and the delivery service retries a failed
delivery on its own with backoff, so a row with status: failed is not
yet a lost event. manual says whether somebody asked for the attempt
rather than it being automatic.
Attempts are recorded shortly after delivery rather than instantly, so an attempt made a moment ago may not be on this page yet. Read again rather than concluding nothing was tried. The log is a recent record rather than an archive, so keep anything you need to hold on to.
Two things are never returned: whatever a destination authenticates
with, and the response body your endpoint answered with, since a
refusing endpoint routinely echoes the request back inside it, headers
included. response_status stands in for the body, and the delivery
portal has the rest.
Headers
-
The contract version this request is written against. Every version published so far is a release date (
YYYY-MM-DD), which is why the examples look like one, but the value is an opaque identifier: match it against the versions this API publishes rather than parsing it, because a future one may not be only a date. An unknown value answers400withinvalid_version.Omitting the header pins the request to the version that was current when the API key was issued, so an integration keeps working when a newer version ships.
Minimum length is
1.
Path parameters
-
The newsletter's
id(a UUID) or itshandle. A handle is unique across Commune and is the identifier its public web profile uses, so it is the one to hardcode in an integration.
Query parameters
-
The
pagination.next_cursorvalue from the previous page. Omit it to read the first page. A cursor is opaque, is only valid for the same operation with the same filters, and is not a durable identifier.Maximum length is
512. -
How many items to return in this page. This is a page size, not an offset. Fewer items than requested may come back and that does not mean the collection is exhausted, only an absent
next_cursordoes.Minimum value is
1, maximum value is100. Default value is20. -
Comma-separated list of relationship paths to inline in the response. Unexpanded relationships are returned as a reference object carrying only
idandobject. Each operation documents the paths it accepts, and an unknown path answers400. Nested paths use a dot, for examplearticle.newsletter. -
Comma-separated allow-list of top level properties to return on each object, so a client can trim a response it does not need in full.
idandobjectare always returned. An unknown property name answers400. Properties omitted by an operation, such ascontenton any article list, cannot be brought back withfields. -
Return only attempts that ended this way. Omit for both.
Values are
succeededorfailed. -
Return only attempts at delivering this event, by the
idfrom the envelope and from theCommune-Event-Idheader.Maximum length is
128. -
Return only attempts at this destination, by the
idfromGET /newsletters/{newsletter}/destinations.Maximum length is
128.
Responses
-
A page of delivery attempts, most recent first.
Hide response attributes Show response attributes object
-
Always
list, so a response is self describing.Value is
list. -
Cursor pagination state. Commune never exposes an offset or a page number: a collection is a moving window, and an offset silently skips or repeats items when the window shifts between two requests.
Additional properties are NOT allowed.
Hide pagination attributes Show pagination attributes object
-
This page of handovers, most recent first, one entry per attempt at one destination. A retry is a new entry with a higher
attemptrather than an edit to the one before it, so a single event delivered to two destinations and retried once at one of them is three entries here.status: failedis not yet a lost event: the delivery service retries on its own with backoff. Attempts are recorded shortly after delivery rather than instantly, so one made a moment ago may not be here yet. Neither the destination's credentials nor the body it answered with is on an entry.Hide data attributes Show data attributes object
One handover of one event to one destination, and what came of it.
A record of something that happened rather than a thing with a state: it never changes after it is written, and a retry is a second
DeliveryAttemptwith a higherattemptrather than an edit to this one.Two fields a reader might expect are not here. The body your endpoint answered with is never returned, since a refusing endpoint routinely writes the request back into its own response, credentials included. Neither is the event's payload: several topics carry a subscriber's email address, and returning it here would make every read of this log a read of audience data.
event_idnames the event,event_typesays which topic it was, andresponse_statussays what the endpoint answered.-
Always
delivery_attempt.Value is
delivery_attempt. -
The delivery service's identifier for this attempt. Opaque, and not a UUID: it is minted on the other side of the handover.
- newsletter
object | null Required The newsletter whose event this was. A
Refunlessnewsletteris named in?expand=.One of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A newsletter and its public profile. Nothing operational is exposed: ESP credentials, OAuth tokens, group and audience ids, feed polling state and language detection bookkeeping all stay server side.
Hide attributes Show attributes
-
Always
newsletter.Value is
newsletter. -
Stable identifier.
-
The short, unique, URL safe name. Resolves the public profile at
/n/{handle}and is accepted anywhere{newsletter}is. -
Display name, as the creator writes it.
-
The profile blurb. Sanitised HTML, not plain text, because creators format it. Treat it as untrusted markup and render it in a sandboxed context.
-
Where a newsletter is published from.
communemeans Commune itself sends the email. Every other value is an email service provider whose posts Commune imports.rsscovers any feed that is not one of the named providers.Values are
commune,beehiiv,buttondown,ghost,kit,mailchimp,mailerlite,rss, orsubstack. -
Square avatar for the newsletter.
-
The creator's own site, if they linked one.
-
The creator's other homes on the internet, stored as canonical profile URLs. Every key is optional and a newsletter that set none returns an empty object.
Additional properties are NOT allowed.
Hide social_links attributes Show social_links attributes object
-
X or Twitter profile URL.
-
Bluesky profile URL.
-
LinkedIn profile URL.
-
Mastodon profile URL, including the instance host.
-
YouTube channel URL.
-
Instagram profile URL.
-
Threads profile URL.
-
GitHub profile URL.
-
-
Best known language of the newsletter's writing as a BCP 47 tag. Detected from recent articles rather than declared, so treat it as a hint. Null before enough has been published to tell.
-
Who may start a new chat thread in this community.
Values are
editors,subscribers, oranyone. -
Whether people who have not subscribed may reply in existing threads.
- owner
object | null The account that owns the newsletter. A
Refunlessowneris named in?expand=.Any of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A person's public profile, and the whole of what this API returns about anybody other than the credential's own owner. Email address, theme, notification preferences, push subscriptions, read state and saved articles are never carried.
Hide attributes Show attributes
-
Always
user.Value is
user. -
Stable identifier.
-
The unique handle the profile resolves on at
/@{username}. Null for an account that has not finished signing up. -
The name shown next to their messages and bylines.
-
Profile picture. Commune falls back to a generated avatar when the person never set one, so this is rarely null in practice.
-
- featured_article
object | null The article the creator pinned to the top of the profile, or
nullwhen none is pinned. ARefunlessfeatured_articleis named in?expand=.One of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
One article of a newsletter, without its body. Every collection of articles returns this shape.
GET /articles/{article}returnsArticleWithContent, which is this pluscontent.Hide attributes Show attributes
-
Always
article.Value is
article. -
Stable identifier.
-
Eight character base62 identifier, unique across Commune. Safe in a URL and accepted anywhere
{article}is. -
URL segment under the newsletter, unique within it but not across Commune. The permalink is
/n/{handle}/a/{slug}. Falls back to theshort_idfor an untitled article. - newsletter
object | null Required The newsletter this article belongs to. A
Refunlessnewsletteris named in?expand=.One of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A newsletter and its public profile. Nothing operational is exposed: ESP credentials, OAuth tokens, group and audience ids, feed polling state and language detection bookkeeping all stay server side.
Additional properties are NOT allowed.
-
-
Subject line of the article. Null for an untitled draft.
-
The short line email clients show after the subject, and what Commune uses as the excerpt on a card.
-
Cover image. When the creator set none, Commune stamps the first image in the body at send time, so this is usually populated for a sent article.
-
The article's canonical URL on the newsletter's own provider, for an imported article. Null for one written in Commune.
-
Where an article is in its life. A credential holding only
readpermissions ever seessentand nothing else. An imported article is alwayssent, since Commune sees it after the provider delivered it.Values are
draft,scheduled,sending,sent,failed, orarchived. -
truewhen the article came in from the newsletter's provider,falsewhen it was written and sent in Commune. -
When the article went out. An article dated in the future is not returned by any read operation until that moment passes, so this is never ahead of now in a response.
-
When a queued article may go out. Set while
statusisscheduledand null otherwise.This is not
posted_atand the difference matters: a queued article has no publication date yet, which is why it stays invisible on every reader surface until it really goes out. Commune dispatches in passes, so this is the moment from which the article may go rather than the moment it will. -
The byline, in order. Each entry is a
Refunlessauthorsis named in?expand=. Empty when no Commune account is credited.Any of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A person's public profile, and the whole of what this API returns about anybody other than the credential's own owner. Email address, theme, notification preferences, push subscriptions, read state and saved articles are never carried.
Hide attributes Show attributes
-
Always
user.Value is
user. -
Stable identifier.
-
The unique handle the profile resolves on at
/@{username}. Null for an account that has not finished signing up. -
The name shown next to their messages and bylines.
-
Profile picture. Commune falls back to a generated avatar when the person never set one, so this is rarely null in practice.
-
- thread
object | null The chat thread this article opened, where its discussion lives.
nullwhen the newsletter does not open a thread per article. ARefunlessthreadis named in?expand=.One of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A conversation in a newsletter's community, together with the message that opened it. Its replies are a separate collection.
Hide attributes Show attributes
-
Always
thread.Value is
thread. -
Stable identifier.
-
Eight character base62 identifier used by the thread's own URL at
/n/{handle}/chat/{short_id}. Null for a thread Commune opened under an article, which is reached through the article instead. - newsletter
object | null Required The community this thread lives in. A
Refunlessnewsletteris named in?expand=.One of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A newsletter and its public profile. Nothing operational is exposed: ESP credentials, OAuth tokens, group and audience ids, feed polling state and language detection bookkeeping all stay server side.
Additional properties are NOT allowed.
-
- author
object | null Who opened the thread. A
Refunlessauthoris named in?expand=.Any of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
A person's public profile, and the whole of what this API returns about anybody other than the credential's own owner. Email address, theme, notification preferences, push subscriptions, read state and saved articles are never carried.
Hide attributes Show attributes
-
Always
user.Value is
user. -
Stable identifier.
-
The unique handle the profile resolves on at
/@{username}. Null for an account that has not finished signing up. -
The name shown next to their messages and bylines.
-
Profile picture. Commune falls back to a generated avatar when the person never set one, so this is rarely null in practice.
-
-
The opening message. HTML, since people format what they write. Treat it as untrusted markup and render it in a sandboxed context.
-
Attachments on the opening message.
Hide media attributes Show media attributes object
An image or file attached to a thread or a message.
-
Where the attachment is served from.
-
The attachment's media type when Commune recorded one, for example
image/png. Null for an attachment old enough that none was recorded. -
A smaller rendition, when one was generated.
-
-
Where a thread is placed.
publicputs it on the global Commune feed and makes it readable by anyone.subscriberskeeps it inside the newsletter.paidnarrows it further to the paying part of the audience. Set and changed by the newsletter's team.Values are
public,subscribers, orpaid. -
truewhen Commune opened this thread under an article rather than a person starting it. These are kept off the global feed, because the article card already represents the conversation there. - article
object | null The article that opened this thread, when
is_article_threadistrue.nullotherwise. ARefunlessarticleis named in?expand=.One of: An unexpanded relationship. Ask for the relationship in
?expand=to get the full object in its place.Hide attributes Show attributes
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
One article of a newsletter, without its body. Every collection of articles returns this shape.
GET /articles/{article}returnsArticleWithContent, which is this pluscontent. -
-
Undeleted replies in the thread, at any depth.
Minimum value is
0. -
How many times the thread was opened.
Minimum value is
0. -
When the thread was opened.
-
When the thread row last changed for any reason.
-
When the author last edited the opening message. Null when it was never edited, which is what drives the edited marker in the product.
-
When the thread last received a reply, or when it was opened if it never did. This is the sort key for the thread list.
-
-
Engagement counts for an article, computed at read time. These are Commune side counts, not provider side email metrics: opens, clicks and deliveries are not here.
Additional properties are NOT allowed.
Hide stats attributes Show stats attributes object
-
How many people liked the article.
Minimum value is
0. -
Replies in the article's chat thread. Commune has no separate comments store: an article's discussion is a thread like any other, so this counts the undeleted replies hanging off it.
0when the article has no thread.Minimum value is
0. -
How many passages readers highlighted.
Minimum value is
0.
-
-
When the row was created in Commune.
-
When the article was last edited.
-
-
When the newsletter was connected to or created on Commune.
-
When the profile last changed.
-
-
Where this was delivered. Always a
Ref, whoseidmatches a row fromGET /newsletters/{newsletter}/destinations. A destination deleted since the attempt was made still appears here, because the attempt happened; it will not be in that list any more.Additional properties are NOT allowed.
Hide destination attributes Show destination attributes object | null
-
The type of the referenced resource.
-
The referenced resource's
id, in whatever form that resource's own schema declares. Most are UUIDs; aRefwhoseobjectisusercarries an account identifier, which is an opaque string and not a UUID. Compare it for equality and pass it back; do not parse it.
-
-
What kind of target it was, as the delivery service named it at the time. Free text for the same reason
Destination.typeis: the vocabulary belongs to the delivery service and grows there. -
The event that was being delivered, by the
idon its envelope. The same string the consumer receives in theCommune-Event-Idheader, which makes it the one identifier both sides share and the thing worth logging on yours. -
The topic, matching the keys of the
webhooksblock of this document. Null only if the delivery service no longer holds the event this attempt belonged to. -
How this attempt ended.
Values are
succeededorfailed. -
The HTTP status the destination answered with. Null when it did not answer at all, in which case
failuresays why. -
Why there was no answer, when there was none:
timeoutis the common one. Null wheneverresponse_statusis set, and the two are never both set or both null. Free text, so treat an unrecognised value as a reason this client does not know how to describe. -
1 on the first delivery of this event to this destination, and one higher on each retry of it. The number the
Commune-Delivery-Attemptheader would carry if it were sent.Minimum value is
1. -
Whether somebody asked for this attempt rather than the delivery service making it on its own. True for one made by
POST /delivery-attempts/{attempt}/replayor by the retry button in the portal, and false for a first delivery or an automatic retry. -
When the attempt was made.
-
-
-
The request was malformed, and the same request will fail the same way until it is changed.
paramnames the parameter or header at fault when there is exactly one, andallowed_valueslists what it accepts when that is a finite set. The code isbad_requestfor every case below except the last.- A query parameter: one the operation does not have, a value
outside its set, range or format (an unparseable cursor, an unknown
expandpath orfieldsname, an identifier that is not a UUID), or a required one left out, such asqon a search ornewsletterwhen the credential reaches more than one. - The request body: not JSON, not the shape the operation reads,
a property it does not write, or a value of the wrong type, length
or format.
paramis absent here, since the body is not a parameter, and the message names the property. - The
Idempotency-Keyheader, on an operation that changes something: missing, or a value this API will not store. - An unrecognised
Commune-Version, which answers with its own code,invalid_version, because it is never fixed by changing the body.
Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
- A query parameter: one the operation does not have, a value
outside its set, range or format (an unparseable cursor, an unknown
-
No credential was presented, or it is malformed, unknown, revoked or expired, or it is an access token minted for a different audience.
Every one of these answers identically, down to the wording and the headers, so a refusal never confirms that a string was once real.
Hide headers attribute Show headers attribute
-
The authentication scheme this API accepts, and where to find out how to get a credential for it. Always
Bearer realm="Commune API", resource_metadata="https://api.usecommune.com/.well-known/oauth-protected-resource".resource_metadatais the RFC 9728 pointer to this API's protected resource metadata, which names the authorization server an OAuth client should send its user to. A client holding an API key can ignore it. The header carries noerrorparameter, not evenerror="invalid_token", because it describes what this API accepts rather than what was wrong with the credential sent, and the reasons above are deliberately indistinguishable.There is no second scheme and no query-parameter fallback, because a credential that can travel in a URL ends up in access logs and referer headers.
Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
-
-
The credential is valid but is not allowed to do this. Two codes answer with this status, and
error.codesays which.insufficient_scope: it does not hold the permission. The operation needs, say,audience: readon the newsletter addressed, and this credential holds less than that there.allowed_valuescarries the permission that was needed, and the message says what the credential does hold on that newsletter, because a credential granted the wrong family and a credential belonging to somebody whose standing on the team has narrowed look identical without it. The answer can differ per newsletter: the same credential may be allowed here and refused on the next one it reaches.The same code answers an operation that needs the account permission from a credential that does not carry it. That permission is about the person a credential belongs to rather than about any newsletter, so nothing granted on a newsletter adds up to it. It is granted on the credential itself, when a key is minted or when an authorization asks for
account:read.And it answers a parameter the credential may send, but not with the value it sent: a filter a credential holding only
readpermissions may not use, or anexpandpath whose rows need a permission the operation does not.paramnames the parameter, andallowed_valuescarries what this credential may send instead, or is absent when it may send nothing there at all.forbidden: it may not act here at all. Either the credential does not reach the newsletter addressed, because it was never granted it or because the person it belongs to can no longer act on it, or it reaches no newsletter at all;paramisnewsletter, andGET /newsletterslists the ones it does reach. Or, onDELETE /api-keys/{key}, the credential named belongs to somebody else. Neither carriesallowed_values, because there is no value to send instead.Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
-
-
No such resource, or the key is not allowed to know that it exists. Commune answers
404rather than403where distinguishing the two would leak the existence of private content.Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
-
-
Too many requests. Back off and retry after the interval named by the
Retry-Afterresponse header.One of the budgets in
RateLimit-Policyran out, and theRateLimit-*headers on this response say which and when it resets.Hide headers attribute Show headers attribute
Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
-
-
Something failed inside Commune. The request may be retried.
Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
-
-
A capability this operation depends on did not answer. Every other operation is unaffected, so back off on this one rather than on the API.
Two parts of the API can answer this, because they are the only ones Commune cannot serve out of its own database.
Event delivery. Destinations, the attempt log and the portal all live in the delivery service. It is never an empty answer instead, because a destination list or an attempt log that came back empty for this reason reads exactly like a newsletter that has registered no endpoints and sent nothing anywhere.
sendArticleTest. A test copy is sent while the request is open, by Commune's sending service, and this answers when that service could not be reached or when the sending provider refused every address on the test, so nothing arrived. Nothing about the article changes either way, and the message says which of the two happened.Hide headers attribute Show headers attribute
Hide response attribute Show response attribute object
-
Additional properties are NOT allowed.
Hide error attributes Show error attributes object
-
The stable, machine readable reason a request failed. New codes may be added in a minor version, so treat an unrecognised code as a generic failure of its HTTP status class.
Two of these share a status with a neighbour and exist because what a caller does next is different.
invalid_versionis a400that is never fixed by changing the request body.not_commune_newsletteris a422that is never fixed by changing the request at all: it says the newsletter's articles are published somewhere else and mirrored into Commune afterwards, so Commune cannot write one. Its page athttps://usecommune.dev/errors/not_commune_newsletter, like every code's, is itsdocs_url, and it covers moving a newsletter onto Commune's own publishing, which is the only thing that resolves it.Values are
bad_request,invalid_version,unauthorized,forbidden,insufficient_scope,payment_required,not_found,conflict,unprocessable,not_commune_newsletter,rate_limited,internal_error, orservice_unavailable. -
A human readable sentence describing what went wrong. Written for a developer reading a log, not for an end user. Do not branch on it, branch on
code. -
The query, path or body parameter the error is attributed to, when the error is attributable to exactly one. Absent otherwise.
-
Everything
paramwould have accepted, when what it accepts is a finite set. Absent when it is not: a cursor, an identifier or a numeric range has nothing to enumerate, and an empty array would read as "nothing is allowed".It repeats what
messagesays in prose, so a caller can correct a request from this one response: the array is what a program branches on, the sentence is what a person or a model reads.On an unknown parameter name rather than an unknown value, this carries the parameter names the operation does accept, since that is the set the caller has to pick from.
On an
insufficient_scopefailure there is usually no parameter at fault andparamis absent, and this carries the one permission that was needed, written the way the permission table writes it, such ascontent: read. The exception is a credential that may call the operation but not with one value of a parameter, such as?expand=subscriberonlistNewsletterInsightswithoutaudience: read: thenparamnames the parameter and this carries the values this credential may send instead. -
Identifier for this request, echoed in the
Commune-Request-Idresponse header. Quote it in support requests. -
Link to the documentation for this error code: always
https://usecommune.dev/errors/followed by the code, a page on what the code means, what usually causes it and how to fix it.
-
-
curl \
--request GET 'https://api.usecommune.com/newsletters/9a4c1c6e-0f2b-4f47-9d3f-6d1b1a2c3d4e/delivery-attempts' \
--header "Authorization: Bearer $ACCESS_TOKEN" \
--header "Commune-Version: 2026-08-26"
{
"object": "list",
"data": [
{
"object": "delivery_attempt",
"id": "att_5Kd9Rb2mQx",
"newsletter": {
"object": "newsletter",
"id": "7d3f1c02-58a1-4a4e-9a0b-2f6d1c9e4411"
},
"destination": {
"object": "destination",
"id": "des_4Nb8Fy1kLd"
},
"destination_type": "webhook",
"event_id": "018f2a8b-6c4b-7d2e-9f11-6a1c3d5e7b90",
"event_type": "article.published",
"status": "succeeded",
"response_status": 200,
"failure": null,
"attempt": 2,
"manual": false,
"created_at": "2026-08-26T09:33:04Z"
},
{
"object": "delivery_attempt",
"id": "att_2Hf6Vp8sZn",
"newsletter": {
"object": "newsletter",
"id": "7d3f1c02-58a1-4a4e-9a0b-2f6d1c9e4411"
},
"destination": {
"object": "destination",
"id": "des_4Nb8Fy1kLd"
},
"destination_type": "webhook",
"event_id": "018f2a8b-6c4b-7d2e-9f11-6a1c3d5e7b90",
"event_type": "article.published",
"status": "failed",
"response_status": null,
"failure": "timeout",
"attempt": 1,
"manual": false,
"created_at": "2026-08-26T09:32:19Z"
}
],
"pagination": {
"has_more": true,
"next_cursor": "Y3Vyc29yOjE3NTY0MjM2MDAwMDA6MDE5MmM4"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}
# Headers
WWW-Authenticate: string
# Payload
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}
# Headers
Retry-After: 42
# Payload
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}
# Headers
Retry-After: 42
# Payload
{
"error": {
"code": "bad_request",
"message": "Newsletter not found.",
"param": "cursor",
"allowed_values": [
"subscribed",
"unsubscribed",
"bounced",
"complained",
"pending"
],
"request_id": "req_01j9c8h1q7m3n4p5r6s7t8u9v0",
"docs_url": "https://usecommune.dev/errors/not_found"
}
}