Mailspace-logs en herstel
Deze endpoints beantwoorden "wat is er met mijn mail gebeurd?" en "kan ik het terugkrijgen?" voor één mailomgeving: het bezorglogboek, het archief met verwijderde berichten die nog terug te zetten zijn, en de mailboxen waarvan de mailserver de mail nog vasthoudt na een definitieve verwijdering. Het aanschaffen, bekijken, resizen en verwijderen van de mailomgeving zelf staat bij de endpoints op planniveau op Mailspace.
Elk pad op deze pagina begint met /api/mailspace/:mailspace_id/…, waarbij
:mailspace_id de GUID van de mailomgeving is uit
GET /api/mailspace.
OAuth-scopes: reads vereisen mailspace:read, writes vereisen
mailspace:write. Sessie- en API-sleutel-credentials slaan de scopecontroles
volledig over (zie OAuth). Een OAuth-token zonder de
vereiste scope wordt geweigerd met 403
{"error":"insufficient_scope", ...} — de OAuth-foutenvelop, niet de
{"errors":[...],"code":"..."}-envelop die elke andere fout op deze pagina
gebruikt. Zie Scope-handhavingsfouten.
Alles op deze pagina wordt live van de mailserver gelezen — er is geen
lokale tabel die het bezorglogboek of het archief voedt — dus elke aanroep kost
een of meer round trips daarheen. Niets op deze pagina is gepagineerd: page
en per_page worden hier nergens gelezen. De normale
rate limit van de API geldt, dus poll op
menselijke tijdschaal en niet in een lus.
Een leeg resultaat is geen bewijs dat er niets aan de hand is
Drie losse mechanismen op deze pagina veranderen een ontbrekende mogelijkheid of een mislukte aanroep naar de mailserver in een lege lijst in plaats van een fout, en geen van de drie wordt aan de client gemeld:
- bezorgtracing is een Enterprise-functie van de mailserver; waar die niet is gelicentieerd zijn de tracelijsten altijd leeg terwijl de uitgaande wachtrij nog wel gegevens bevat — zie Bezorglogboek;
- het archiveren van verwijderde mail zit net zo achter Enterprise, en waar het niet beschikbaar is leest het archief als leeg — zie Archief met verwijderde mail;
- een onbereikbare of falende mailserver zakt bij de arrays van het
bezorglogboek én bij de archieflijst terug naar leeg in plaats van een
5xx, omdat die readers de fout opslokken en intern loggen. Alleen de lijst met herstelbare mailboxen antwoordt hier met503— zie Leesfouten.
Er is geen enkele manier om die mogelijkheden op te vragen. Deze API kan je niet vertellen op welke tier de mailserver draait, en gokt er niet naar. Toon nooit "geen bezorgproblemen" of "niets te herstellen" op grond van een lege reactie alleen.
Gedeelde controles
Elk endpoint op deze pagina erft dezelfde keten van controles, in deze volgorde toegepast voordat de actie draait. De eerste controle die faalt, beantwoordt het verzoek.
| Geldt voor | Voorwaarde | Reactie |
|---|---|---|
| alle | mailhosting is niet geconfigureerd op het platform | 503 stalwart_unavailable |
| alle | :mailspace_id onbekend, of niet zichtbaar voor je credential |
404, lege body |
| writes | gebruiker heeft geen bewerkrecht op de eigen workspace van de mailomgeving | 403 not_authorized |
| alle | mailomgeving is geblokkeerd (door support of wegens een onbetaalde factuur) | 403 mailspace_suspended |
| writes | mailomgeving is met soft delete verwijderd (wacht op verwijdering) | 403 pending_delete |
| alle | mailomgeving is nog niet voorzien | 409 not_provisioned |
Een write wordt bepaald door het HTTP-verb, niet door het endpoint
De twee write-controles beslissen op basis van de request-methode: GET en
HEAD gaan er zo door, al het andere wordt gecontroleerd. Op deze pagina is
Een gearchiveerd item terugzetten dus een
write omdat het een POST is, en is
Een gearchiveerd bericht downloaden een
read omdat het een GET is — ook al levert de download een volledig bericht op.
Een lid met alleen leesrechten dat een mailspace:write-token heeft, kan
daarom het bezorglogboek lezen, het archief doorzoeken, berichten downloaden en
herstelbare mailboxen opvragen, en krijgt 403 not_authorized bij elk
terugzetten en verwijderen: aan de scope van het token is voldaan, aan het
recht van het lid niet.
HEAD wordt precies als GET behandeld, dus een read-endpoint met HEAD
aftasten geeft dezelfde status terug als de GET zou doen.
Reads overleven de bewaartermijn, writes niet
Een met soft delete verwijderde mailomgeving (die op verwijderen wacht) blijft
de hele bewaartermijn reads beantwoorden — je kunt haar logs nog lezen,
haar gearchiveerde mail opvragen en haar herstelbare mailboxen opvragen. Elke
mutatie wordt geweigerd met 403 pending_delete totdat de mailomgeving is
hersteld. Zie
Een mailomgeving verwijderen.
Een geblokkeerde mailomgeving gedraagt zich anders: mailspace_suspended
blokkeert reads net zo goed als writes. Een mailomgeving die op verwijderen
wacht is van die controle uitgezonderd, dus de twee codes gelden nooit samen.
Een onbekende of buiten je bereik vallende :mailspace_id antwoordt 404 met een
lege body, niet met de gebruikelijke foutenvelop — een GUID die bij een andere
workspace hoort moet niet te onderscheiden zijn van een die niet bestaat.
Bezorglogboek
GET /api/mailspace/:mailspace_id/logs
Scope mailspace:read.
Alles wat de mailserver weet over de recente mail van deze mailomgeving, in vier
losse arrays: de bezorgtraces incoming en outgoing, de live uitgaande queue,
en issues — de afgeleide lijst van bezorgingen die zijn mislukt of nog opnieuw
worden geprobeerd.
Dit endpoint neemt helemaal geen parameters: geen filters, geen datumbereik,
geen paginering. De reader haalt per dataset maximaal de 100 meest recente
entries op — maximaal 100 traces, die daarna over incoming en outgoing worden
verdeeld (interne mail telt in beide mee), maximaal 100 wachtrij-entries en
maximaal 100 issues — en de mailserver heeft niets om door te pagineren. Intern
vraagt één aanroep de traces en de wachtrij één keer op per domein dat de
mailomgeving host, dus het zijn meerdere round trips: dit is geen endpoint om kort
achter elkaar te pollen.
De vier arrays zijn niet even goed beschikbaar — lees dit voordat je erop bouwt
Ze komen uit verschillende objecten op de mailserver, en juist daarom worden ze onder aparte keys teruggegeven in plaats van samengevoegd in één lijst:
incoming/outgoingkomen uit de bezorgtracing van de mailserver, en dat is een functie alleen voor Enterprise. Waar die niet is gelicentieerd zijn beide arrays altijd leeg, hoeveel mail er ook is verwerkt.queuekomt uit de uitgaande wachtrij, die op elke build bestaat. Die bevat gegevens, ongeacht de licentie.issuesis de vereniging van de twee — permanente fouten komen uit de traces, berichten die nog opnieuw worden geprobeerd komen uit de wachtrij — dus die is gedeeltelijk aangetast waar tracing niet beschikbaar is: berichten die nog opnieuw worden geprobeerd staan er wel in, afgeronde bounces niet.
Doordat ze apart blijven, kan een client de degradatie zien: een gevulde
queue terwijl beide trace-arrays leeg zijn, betekent dat tracing op deze
installatie niet beschikbaar is, niet dat er geen mail is verwerkt.
Samengevoegd zou "Enterprise niet gelicentieerd" niet te onderscheiden zijn
geweest van "geen mailproblemen".
Een client mag lege trace-arrays niet lezen als "geen mailproblemen", en er
is geen vlag die je in plaats daarvan kunt controleren — de API krijgt niet
te horen op welke tier de mailserver zit, dus die kan het je niet vertellen.
Moet je integratie de twee onderscheiden, vergelijk dan de trace-arrays met
queue en behandel "traces blijvend leeg" als een eigenschap van de
installatie.
Interne mail staat er twee keer in, met opzet
Een bericht van de ene mailbox naar de andere binnen dezelfde mailomgeving
is echt beide richtingen: het loopt via de lokale wachtrij weg, dus het
classificeert als inkomend, terwijl iemand in de mailomgeving het wel degelijk
heeft verstuurd. Het staat daarom onder zowel incoming als outgoing,
met internal: true erbij zodat je het kunt labelen in plaats van dubbel te
tellen. Zonder dat zou een klant die een bericht zoekt dat hij een collega
stuurde, het nooit onder Uitgaand vinden.
Teruggegeven params
- incoming:
Array<Object>| traces voor mail die aan de mailboxen van deze mailomgeving is bezorgd - outgoing:
Array<Object>| traces van bezorgpogingen naar andere mailservers, plus elk intern bericht - queue:
Array<Object>| berichten die nog in de uitgaande wachtrij staan - issues:
Array<Object>| permanente fouten (uit de traces) en berichten die nog opnieuw worden geprobeerd (uit de wachtrij)
Elke entry in incoming en outgoing is een trace:
- id: String |
null— de trace-id van de mailserver, zonder controle doorgegeven, dus een trace die de mailserver zonder id vastlegde komt binnen alsnull. Gebruik dit veld niet zonder controle als sleutel of om de tracearrays te ontdubbelen — de garantie dat het nooitnullis geldt voorqueueenissueshieronder, en niet hier - timestamp: String | ISO 8601, zoals de mailserver het heeft vastgelegd
- from: String | de envelope-afzender, zoals de mailserver die heeft vastgelegd
- to: String | ontvangers als komma-gescheiden string, niet als array. Bij mail van een afzender buiten deze mailomgeving worden mede-ontvangers op domeinen die deze mailomgeving niet host verwijderd voordat je het ziet
- subject: String |
nullals de mailserver er geen heeft vastgelegd - size: Integer |
null— bytes - direction: String |
incomingofoutgoing - internal: Boolean |
truevoor mail van mailbox naar mailbox binnen deze mailomgeving (zie hierboven) - status: String | afgeleid uit het SMTP-log:
delivered,bounced,failed,retryingofsending - events:
Array<Object>| het SMTP-log, in de volgorde waarin de mailserver het heeft vastgelegd- name: String | de naam van de mailserver-event, bijv.
delivery.attempt-start - timestamp: String | ISO 8601
- key_values:
Array<Object>| de detailvelden van de event, als lijst in plaats van als object omdat dezelfde key legitiem kan terugkomen binnen één event- key: String
- value: String, Integer of Boolean | onbewerkt doorgegeven van de mailserver
- name: String | de naam van de mailserver-event, bijv.
Elke entry in queue en issues is een bericht in de wachtrij:
- id: String | de eigen wachtrij-id van de mailserver bij een wachtrij-entry, en een stabiele kunstmatige
trace-<…>-id bij een issue dat uit een trace is afgeleid. Nooitnullin deze twee arrays — die garantie geldt specifiek voor deze twee en niet voor de eigenidvan een trace. Zie de waarschuwing hieronder - source: String |
queueoftrace— uit welke dataset de entry komt. Aanwezig op elke entry van beide arraysqueueenissues; de trace-arrays bevatten het veld niet. Zie de waarschuwing hieronder voor waar het voor dient - created_at: String | ISO 8601
- return_path: String | de envelope-afzender. Leeg betekent een null return path (
<>): het bericht is een bouncemelding - subject: String |
null - size: Integer |
null— bytes - flags:
Array<String>| vlaggen van de mailserver, bijv.dsnSentzodra er een bouncemelding is verstuurd - recipients:
Array<Object>- address: String
- status: String |
Completed,TemporaryFailureofPermanentFailure;nullzolang de ontvanger nog in de wachtrij staat en er niets is geprobeerd - retry_due: String |
null— ISO 8601, wanneer de volgende poging volgt - retry_count: Integer |
null - error: Object |
nullwanneer de mailserver geen foutdetails voor die ontvanger heeft gemeld- type: String |
null— het fouttype van de mailserver - response_code: Integer |
null— de SMTP-statuscode. Wordt onbewerkt doorgegeven, dus een issue dat uit een trace komt bevat wat het SMTP-log heeft vastgelegd en kan een String teruggeven - response_enhanced: String |
null— de enhanced status code, bijv.5.1.1 - response_hostname: String |
null— de server die antwoordde - message: String |
null— de tekst van de andere server
- type: String |
- events:
Array<Object>| het SMTP-log in dezelfde vorm als bij een trace. Leeg bij een live wachtrij-entry, die nog geen trace heeft; alleen gevuld bij een issue dat uit een trace komt
Leid de status op berichtniveau zelf af
Een bericht in de wachtrij heeft geen status-veld op het hoogste niveau —
bewust, zodat de regel op één plek staat in plaats van gedupliceerd te worden.
Leid hem af uit recipients en flags:
- een ontvanger op
PermanentFailure→bouncedalsflagsdsnSentbevat, andersfailed; - anders een ontvanger op
TemporaryFailure→retrying; - anders alle ontvangers op
Completed→delivered; - anders →
sending.
Een issues-entry is geen wachtrij-entry, ook al heeft die dezelfde vorm
issues wordt weergegeven in de vorm van een bericht in de wachtrij, uit welke
bron het ook komt; dat maakt het uniform om te tonen maar makkelijk om te
overinterpreteren:
- Een issue dat uit een trace is afgeleid (een afgeronde bounce of
weigering) heeft geen wachtrij-id om mee te dragen en krijgt daarom een
stabiele kunstmatige
trace-<…>-id — op elke poll dezelfde waarde, dus een client mag erop sleutelen of dedupliceren. Zijnrecipients-array bevat één kunstmatige entry waarvan deaddressde hele komma-gescheiden ontvangerstring van de trace is, metstatusPermanentFailure,retry_dueenretry_countopnull, enerror.typeopnull. Zijnflagsis["dsnSent"]wanneer er een bouncemelding is verstuurd en[]wanneer het bericht zonder melding mislukte, en het bevat het volledige SMTP-log inevents. - Een issue dat uit de wachtrij komt is een echte wachtrij-entry, vermeld
met alleen zijn falende ontvangers — zijn
recipients-array is dus een subset van die onderqueue— en zijneventsis leeg.
source onderscheidt de twee zonder dat je ze hoeft te inspecteren. Elke
entry van queue en van issues bevat het veld: "queue" voor een rij die
uit de live uitgaande wachtrij komt, "trace" voor een rij die uit een trace
is afgeleid. Het zegt of de rij nog kan veranderen — een wachtrij-entry wordt
nog opnieuw geprobeerd, terwijl een issue uit een trace definitief is: de
bouncemelding is al verstuurd en het bericht is al uit de wachtrij verdwenen —
en het is de enige manier om te zien welke entries een events-log bevatten.
De trace-arrays (incoming en outgoing) bevatten het niet: alleen de
rijen in wachtrijvorm krijgen het stempel.
Permanente fouten komen altijd alleen uit traces en berichten die nog opnieuw worden geprobeerd altijd alleen uit de wachtrij, dus de twee bronnen tellen hetzelfde bericht nooit dubbel. Dat betekent ook dat afgeronde bounces bij de dingen horen die verdwijnen waar tracing niet is gelicentieerd.
curl -H "Authorization: Bearer $CLOUDPRESS_TOKEN" \
-H "X-Auth-Account: $ACCOUNT_ID" \
https://my.cloudpress.com/api/mailspace/$MAILSPACE_ID/logs
Archief met verwijderde mail
Mail die een gebruiker heeft verwijderd en die de mailserver nog vasthoudt. Een item
komt hier terecht wanneer een bericht wordt verwijderd, en blijft tot zijn
archived_until-deadline, waarna de mailserver het definitief opruimt. De
bewaartermijn geldt serverbreed en kan via deze API niet worden verlengd of
verkort.
Een leeg archief betekent twee dingen, geen drie
Het archief is een object alleen voor Enterprise op de mailserver, en de
lijst erachter is strikt: een query die niet kon worden uitgevoerd antwoordt
503 archived_items_unavailable.
Gearchiveerde mail opvragen antwoordt 200 met
een lege archived_items-array in twee situaties, en niets in de reactie
onderscheidt ze:
- er is niets gearchiveerd;
- de mailserver licentieert archiveren niet. Er is geen manier om die
mogelijkheden op te vragen, dus "niet gelicentieerd" en "niets gearchiveerd"
zijn bewust niet te onderscheiden — en elk endpoint op id antwoordt dan
404unknown_archived_item.
"De mailserver kon niet gevraagd worden" is niet langer een van die
situaties: dat is de 503. Dat de licentiegrens nog wel terugzakt naar een lege
lijst komt doordat de mailserver een verzoek om een gelicentieerd object wel
beantwoordt, en een antwoord is precies waar de strikte read op controleert;
een server die geen antwoord geeft levert de 503. Een onbereikbare
mailserver is dus wél te onderscheiden van een leeg archief. Een
niet-gelicentieerde niet.
Behandel een leeg archief als "niets te tonen", nooit als "er is nooit iets verwijderd".
Item-id's zijn opaak, en op de mailserver zijn ze globaal
Een gearchiveerd item wordt geadresseerd met het :stalwart_id-segment, de
opake id uit de lijst — geen GUID en niets dat je zelf kunt samenstellen. Op
de mailserver is het een ongescope handle, dus CloudPress resolvet elk id
bij elk verzoek op id opnieuw tegen de eigen mailboxen van deze mailomgeving en
antwoordt 404 unknown_archived_item wanneer het hier niet thuishoort. Een
item-id uit een andere workspace is op alle vier de endpoints op id
onbereikbaar, download inbegrepen.
Vertak op code, nooit op errors[0]
Alle drie de acties die tegen het archief kunnen falen — Gearchiveerde mail opvragen, Een gearchiveerd item terugzetten en Een gearchiveerd item verwijderen — beantwoorden een fout met een zorgvuldig gekozen Engelse zin en geven nooit de eigen fouttekst van de mailserver door; die is interne techniek waar een client niets mee kan.
Die berichten zijn Engels, welke Accept-Language je ook stuurt, ook al zet
die header nog steeds de taal voor de rest van het verzoek, en de formulering
mag veranderen. De code is het contract.
Gearchiveerde mail opvragen
GET /api/mailspace/:mailspace_id/archived_items
Scope mailspace:read.
Elk terug te zetten verwijderd bericht in alle mailboxen van de mailomgeving, het laatst gearchiveerde eerst. Het archief wordt per mailbox opgevraagd en gefilterd op de eigen mailboxen van deze mailomgeving, dus het kan nooit de verwijderde mail van een andere tenant tonen. Niet gepagineerd: CloudPress begrenst de query op 500 items per mailbox, en alles daarboven valt weg zonder markering in de reactie.
Teruggegeven params
- archived_items: Array
- id: String | de opake item-id — dit is het
:stalwart_id-segment voor elk endpoint op id hieronder - type: String | de variant van het item. In de praktijk
Email; de mailserver kent andere maar schrijft ze nooit - status: String |
archived, ofrequestRestorezodra er een terugzetting voor is ingepland. De mailserver laat het veld weg zolang het op zijn standaardwaarde staat, dus het wordt ingevuld alsarchived - subject: String |
null - from: String |
null - size: Integer |
null— bytes - received_at: String |
null— ISO 8601, wanneer het bericht origineel aankwam - archived_at: String |
null— ISO 8601, wanneer het naar het archief is verwijderd - archived_until: String |
null— ISO 8601, de deadline voor definitieve verwijdering. Serverbrede bewaartermijn, niet per item - mailbox_email: String | het adres waaruit het bericht is verwijderd
- mailbox_id: String | de GUID van die mailbox
- id: String | de opake item-id — dit is het
De blob- en account-handles van het item op de mailserver worden bewust niet meegegeven: beide zijn globale, ongescope handles, en het bericht zelf komt terug via Een gearchiveerd bericht downloaden.
Fouten
- 503
archived_items_unavailable| de archiefquery kon niet worden uitgevoerd. Geen clientfout, en dus opnieuw te proberen — zie Leesfouten. Een ongelicentieerd archief zakt nog wel terug naar de lege200die hierboven staat; een onbereikbare mailserver niet - plus de gedeelde controles
Een gearchiveerd item bekijken
GET /api/mailspace/:mailspace_id/archived_items/:stalwart_id
Scope mailspace:read. Eén item, met dezelfde velden als de lijst, onder een
archived_item-object.
De body van het bericht komt hier niet terug — gebruik Een gearchiveerd bericht downloaden voor het originele bericht.
Fouten
- 404
unknown_archived_item| zo'n item bestaat niet, of het hoort niet bij deze mailomgeving - 503
archived_item_lookup_unavailable| de eigendomscontrole kon niet worden uitgevoerd, dus het item is niet bevestigd en niet ontkend. Het opnieuw proberen waard, en geen teken dat het item weg is - plus de gedeelde controles
Een gearchiveerd item terugzetten
POST /api/mailspace/:mailspace_id/archived_items/:stalwart_id/restore
Scope mailspace:write (het is een POST, dus het wordt als write gecontroleerd —
zie hierboven).
Dit meldt een verzoek, geen afgeronde terugzetting
De mailserver kent geen synchrone terugzetting. De trigger is het zetten van
de status van het item op een terugzetverzoek, dat de server vervolgens op zijn
eigen schema verwerkt, en hij geeft geen signaal wanneer het klaar is —
daarom zegt de reactie restore_queued en nooit restored, en heeft deze API
niets om te pollen.
Wil je weten of een terugzetting daadwerkelijk is gebeurd, lees het archief dan opnieuw: een item dat is teruggezet staat er niet meer in, en het bericht komt onder een nieuw id terug in de mailbox. Meld een terugzetting niet als afgerond op grond van deze reactie.
Teruggegeven params
- status: String | altijd
"restore_queued" - archived_item: Object | de velden van het item, exact als in de lijst
Twee verschillende status-velden
De status op het hoogste niveau beschrijft jouw verzoek
(restore_queued). De status binnen archived_item is het eigen veld van
het item, zoals het stond toen het item werd opgezocht — dus een eerste
terugzetting laat hier archived zien, ook al is het verzoek geaccepteerd, en
een tweede op hetzelfde item laat requestRestore zien. Lees het item opnieuw
in plaats van dit veld als de uitkomst te lezen.
Fouten
- 404
unknown_archived_item| zo'n item bestaat niet, of het hoort niet bij deze mailomgeving. Ook wat je krijgt als het item tussen het opzoeken en de terugzetting zelf verdwijnt — zie de note hieronder - 422
restore_failed| de mailserver heeft geantwoord en de terugzetting geweigerd. Er is niets in de wachtrij gezet - 503
archived_item_lookup_unavailable| de eigendomscontrole kon niet worden uitgevoerd, dus er is niets verstuurd naar de mailserver. Het item is onaangeroerd - 503
restore_unconfirmed| onbepaald. De herstelopdracht is verstuurd en de uitkomst is onbekend, dus de terugzetting staat mogelijk al in de wachtrij - plus de gedeelde controles, inclusief
403not_authorizeden403pending_delete
Twee 503-codes die tegengestelde dingen beloven
archived_item_lookup_unavailable betekent dat er niets is geprobeerd.
restore_unconfirmed betekent dat er wél iets is geprobeerd en dat de uitkomst
onbekend is — lees het item, of het archief, dus opnieuw voordat je het opnieuw
probeert, in plaats van aan te nemen dat de terugzetting is mislukt.
Dit is de tegengestelde belofte van
restore_unavailable bij het
herstel van een definitief verwijderde mailbox, dat betekent dat er niets is
gebeurd. Generaliseer niet over die twee vlakken op grond van het woord
"unavailable" — lees de code.
De race met een verdwenen item antwoordt 404, niet 422 — een bewuste wijziging van het contract
De id van het item wordt twee keer geresolveerd: één keer door de gedeelde
controle voordat de actie draait, en nog een keer binnen de terugzetting of de
wissing zelf. Een item dat tussen die twee verdwijnt — realistisch gezien een
client die zijn eigen verlopen verzoek opnieuw doet — antwoordt nu 404
unknown_archived_item, dezelfde code die de controle zelf teruggeeft bij een
misser, omdat het hetzelfde feit is.
Voorheen antwoordde dit 422 restore_failed, en 422 delete_failed bij
Een gearchiveerd item verwijderen. Dat is
bewust gewijzigd nu deze endpoints nog niet in productie draaien: een client
die op 422 vertakt voor een verdwenen item zal dat niet meer zien.
restore_failed en delete_failed houden hun 422 voor een echte weigering
door de mailserver.
Een gearchiveerd bericht downloaden
GET /api/mailspace/:mailspace_id/archived_items/:stalwart_id/download
Scope mailspace:read — een GET, dus een read voor zowel de scope als de
write-controles, ook al levert het een heel bericht op.
Dit is het enige endpoint hier dat geen JSON teruggeeft
Een geslaagde download is het originele RFC822-bericht als ruwe bytes:
Content-Type: message/rfc822 met een attachment-disposition. Het is geen
base64 in een JSON-envelop — dat zou de payload opblazen en elke client dwingen
te decoderen. De bytes gaan via CloudPress in plaats van rechtstreeks naar de
mailserver te linken, omdat de eigen download-URL van de mailserver niet op
jouw mailomgeving is gescopet.
Fouten zijn nog steeds JSON, in de gebruikelijke
{"errors":[...],"code":"..."}-envelop, dus vertak op de responsestatus voordat
je de body als bericht behandelt.
De bestandsnaam van de bijlage wordt afgeleid uit het eigen opgeslagen onderwerp
van het bericht en daarna opgeschoond, zodat een kwaadwillend onderwerp het
opgeslagen bestand niet kan sturen en niet uit de header kan ontsnappen: elk teken
buiten letters, cijfers, underscores, spaties, streepjes en punten wordt _,
reeksen _ worden er één, een punt aan het begin verdwijnt, en spaties aan begin en
eind gaan eraf. Blijft er dan niets over — het bericht had helemaal geen onderwerp,
of het opschonen maakte de stam leeg — dan wordt de item-id gebruikt, dus de
bestandsnaam is nooit een kale .eml. Wat er ook wordt gebruikt, het wordt daarna
afgekapt op 80 tekens voordat .eml eraan wordt geplakt.
De bestandsnaam varieert niet met Accept-Language. Hij komt uit het onderwerp
zoals de mailserver het heeft opgeslagen, nooit uit een vertaald label voor "geen
onderwerp", dus hetzelfde item downloadt voor elke client onder dezelfde naam.
curl -o message.eml \
-H "Authorization: Bearer $CLOUDPRESS_TOKEN" \
-H "X-Auth-Account: $ACCOUNT_ID" \
https://my.cloudpress.com/api/mailspace/$MAILSPACE_ID/archived_items/$ITEM_ID/download
Fouten
- 404
unknown_archived_item| zo'n item bestaat niet, of het hoort niet bij deze mailomgeving - 404 / 503
download_unavailable| het bericht kon niet worden geleverd, en de status is het verschil:404wanneer de mailserver heeft geantwoord dat het opgeslagen bericht weg is (definitief — stop),503bij elke andere leesfout (het bericht kan er nog zijn — probeer het opnieuw). Eén code, omdat het één feit over één resource is; de status zegt wat je nu moet doen - 503
archived_item_lookup_unavailable| de eigendomscontrole kon niet worden uitgevoerd - plus de gedeelde controles
Een gearchiveerd item verwijderen
DELETE /api/mailspace/:mailspace_id/archived_items/:stalwart_id
Scope mailspace:write.
Onomkeerbaar — er zit geen tweede archief achter dit archief
Dit wist het gearchiveerde bericht en de opgeslagen kopie ervan vóór de bewaardeadline. Daarna is er niets meer te herstellen, en er is geen bevestigingsparameter: de enige voorwaarde is dat het item bij deze mailomgeving hoort.
Elke geslaagde wissing wordt met de gebruikte credential vastgelegd in het auditlog van het platform, nadat de mailserver het heeft bevestigd — een logregel voor een vernietiging die niet is gebeurd zou erger zijn dan geen regel.
Een item dat al weg is antwoordt 404 unknown_archived_item, niet 200 — de id
wordt tegen de mailserver geresolveerd voordat er iets wordt verwijderd, dus een
dubbele DELETE en een item dat op zijn eigen deadline is opgeruimd komen beide daar
terecht. Vanuit het oogpunt van de client is dit endpoint niet idempotent.
Teruggegeven params
- deleted: Boolean | altijd
true - id: String | het item dat is gewist
- subject: String |
null— vastgelegd vóór de wissing
Fouten
- 404
unknown_archived_item| zo'n item bestaat niet, of het hoort niet bij deze mailomgeving. Ook wat je krijgt als het item tussen het opzoeken en de wissing zelf verdwijnt — zie de note hierboven - 422
delete_failed| de mailserver heeft geantwoord en de wissing geweigerd. Er is niets vernietigd - 503
archived_item_lookup_unavailable| de eigendomscontrole kon niet worden uitgevoerd, dus er is niets verstuurd. Het item is onaangeroerd - 503
delete_unconfirmed| onbepaald, en deze wissing is onomkeerbaar: het item kan al weg zijn - plus de gedeelde controles
Lees bij delete_unconfirmed eerst het item terug voordat je iets anders doet
Het is de enige plek op dit vlak waar een onbekende uitkomst niet terug te
draaien is, dus dit behandelen als "het verwijderen is mislukt" is de slechtste
van de drie mogelijke aannames. Vraag het item op: een 404 betekent dat de
wissing is doorgekomen.
Bij geen van beide fouten wordt een auditregel geschreven. Die regel wordt pas geschreven nadat de mailserver de wissing bevestigt, dus het ontbreken ervan is geen bewijs dat het item er nog is.
Definitief verwijderde mailboxen
Mailboxen die CloudPress al definitief heeft verwijderd — het lokale record is weg — en waarvan de mailserver de mail nog vasthoudt, omdat het verwijderen van een mailaccount niets meteen wist: het account verdwijnt en er wordt een wistaak ingepland voor het eind van de bewaartermijn van de server. Tot die taak afgaat, kan het account met zijn oorspronkelijke id worden teruggehaald.
Deze horen bewust niet bij de mailboxlijst van de mailomgeving, die dat bakje volledig weglaat — daarom hebben ze hun eigen endpoint.
Herstelbare mailboxen opvragen
GET /api/mailspace/:mailspace_id/purged_mailboxes
Scope mailspace:read.
De definitief verwijderde mailboxen die nog hersteld kunnen worden, geordend op
recoverable_until zodat de mailbox die op het punt staat verloren te gaan eerst
komt. Niet gepagineerd.
De lijst wordt tegen de mailserver gecontroleerd en niet alleen uit de lokale status geleverd: de bewaartermijn is een serverbrede instelling die support kan verkorten, en een vervroegde wissing verwijdert een account meteen — beide laten een lokaal record achter waarvan de deadline nog in de toekomst ligt terwijl de mail al weg is. Een record waarvoor de mailserver geen wistaak meer heeft, wordt volledig weggelaten, omdat een herstel aanbieden dat niet kan worden nagekomen erger is dan er geen aanbieden. De controle kost twee round trips — de wistaken, en de domeinlookup die ze resolvet — en helemaal geen als er niets te controleren is.
Een genoemde mailbox is echt; een lege lijst is geen bewijs dat de mail weg is
De controle is een strikte lees, dus een mailserver die helemaal niet
gevraagd kan worden antwoordt 503 purged_mailboxes_unavailable in plaats
van een lege 200 — zie Leesfouten. Dat sluit het
ene gat, maar niet het andere.
De wistaken worden 500 rijen per keer over de hele mailserver gelezen en
daarna teruggefilterd naar deze mailomgeving, en de reactie bevat geen totaal
en geen signaal dat er is afgekapt. Op een drukke server kunnen de taken van
deze mailomgeving buiten dat venster vallen, waardoor een mailbox die de
mailserver nog zou herstellen stil ontbreekt in een volkomen gezonde 200.
Lees de lijst dus zoals het veilig is: een record dat erin staat is
werkelijk herstelbaar, en een lege lijst betekent "niets gevonden", niet "de
mail is weg". Gooi op grond daarvan nooit je eigen laatste record van een
mailbox weg — houd een kopie van wat je verwijderde tot de eigen
recoverable_until van dat record is verstreken.
Teruggegeven params
- purged_mailboxes: Array
- id: String | de GUID van het record — dit is het
:guid-segment dat Een definitief verwijderde mailbox herstellen neemt - email: String | het adres dat zou terugkomen
- display_name: String |
null - purged_at: DateTime | wanneer CloudPress de mailbox definitief verwijderde
- recoverable_until: DateTime | de eigen wisdeadline van de mailserver, bij het verwijderen gespiegeld — nooit een lokaal berekende termijn
- days_remaining: Integer | hele dagen die resteren, met
0als ondergrens
- id: String | de GUID van het record — dit is het
De opgeslagen quota, aliassen, lidmaatschappen en de account-id op de mailserver worden bewust niet meegegeven: dat is input voor het herstel, geen beschrijving van de verwijderde mailbox.
Fouten
- 503
purged_mailboxes_unavailable| de mailserver kon niet gevraagd worden welke accounts hij nog vasthoudt, dus de lijst kan niet worden beantwoord. Opnieuw te proberen, en bewust geen lege200— een client zou die lezen als "niets te herstellen" - plus de gedeelde controles
Een definitief verwijderde mailbox herstellen
PATCH /api/mailspace/:mailspace_id/purged_mailboxes/:guid
Scope mailspace:write.
Annuleert de openstaande wissing op de mailserver en bouwt de mailbox opnieuw op. De mailserver reconstrueert het account uitsluitend uit de wistaak, die alleen zijn naam en domein bevat, dus al het andere wordt teruggeschreven uit de momentopname die CloudPress bij het verwijderen bewaarde.
password is verplicht, omdat een herstelde mailbox zonder inloggegevens terugkomt
De mailserver geeft het account helemaal zonder wachtwoord terug, dus dit
endpoint stelt er een in. Daarmee is het een write die inloggegevens uitdeelt:
het levert een werkende mailbox op een adres dat mail ontvangt. Een ontbrekend
of leeg password wordt geweigerd met 400 password_blank voordat er iets
wordt aangeraakt, en elk succes wordt met de gebruikte credential vastgelegd
in het auditlog van het platform.
Params
- password: String (required) | het wachtwoord waarmee de herstelde mailbox terugkomt. Een lege of ontbrekende waarde is
400password_blank
De mailbox komt terug met zijn weergavenaam en met zijn opgeslagen quota — die wordt naar beneden bijgesteld in plaats van geweigerd als het pakket er geen ruimte meer voor heeft, dus een mailbox kan iets kleiner terugkomen dan hij was.
Zijn aliassen worden op dezelfde voorwaarden opnieuw toegepast als zijn
lidmaatschappen hieronder: de aliasschrijfactie laat stilzwijgend elk adres vallen
waarvan het domein niet kan worden geresolveerd, en meldt toch succes. Een 200 is
geen bewijs dat elke alias terug is — lees de mailbox terug en vergelijk.
Lidmaatschappen van groepen en mailinglijsten worden opnieuw toegepast, maar zijn niet gegarandeerd
De lidmaatschappen van groepen en mailinglijsten uit de momentopname worden
teruggeschreven waar die groepen en lijsten nog bestaan; een die sindsdien is
verwijderd wordt overgeslagen, en een fout tijdens het opnieuw toepassen laat
het herstel niet mislukken. Een 200 hier betekent dus niet dat de
lidmaatschappen terug zijn. Lees de groepen en lijsten van de mailbox daarna na
en voeg toe wat ontbreekt.
Het record wordt bij succes verbruikt, dus een tweede PATCH op dezelfde :guid
antwoordt 404 unknown_purged_mailbox.
Teruggegeven params
- mailbox: Object | dezelfde vorm die de endpoints voor het verwijderen en herstellen van een mailbox teruggeven
- id: String | de GUID van de herstelde mailbox
- email: String
- status: String |
active - scheduled_deletion_at: DateTime |
null - days_until_deletion: Integer |
null
Een mailbox kan onherstelbaar worden terwijl hij nog in de lijst staat
Herstelbaarheid is een beslissing van de mailserver, en die kan tussen je lijstaanroep en je herstel veranderen. Er zijn drie uitkomsten, en alleen de laatste is het opnieuw proberen waard:
- De eigen deadline van het record is verstreken →
409not_recoverable. Er is niets aangeraakt, en dit is met opnieuw proberen niet tot een succes te maken. - De mailserver is gevraagd en antwoordde dat hij geen wistaak meer heeft
voor het account (de bewaartermijn is verkort, of het account is op verzoek
gewist) →
409not_recoverable, en de momentopname wordt weggegooid, dus dezelfde:guidantwoordt bij een nieuwe poging404unknown_purged_mailboxen de mailbox valt uit de lijst. Ook dit is niet tot een succes te maken — de mail is werkelijk weg. Het deelt de code, de status en de formulering met het geval van de verstreken deadline hierboven, omdat het hetzelfde feit is: definitief, niet opnieuw proberen. - De mailserver kon niet gevraagd worden — de voorafgaande lees mislukte,
of kwam afgezwakt terug →
503restore_unavailable, en de momentopname blijft precies staan waar hij stond. Probeer het opnieuw.
Die lees is bewust strikt, en de momentopname is de enige kopie van de quota, weergavenaam, aliassen en lidmaatschappen van de mailbox: een mislukte lees mag nooit worden aangezien voor "de mailserver heeft hem gewist" en het record vernietigen van een mailbox die de server nog zou teruggeven. De momentopname wordt daarom alleen na een geslaagde lees weggegooid — nooit na een mislukte.
Vertak op de status, en de drie antwoorden op drie verschillende vragen: 409
betekent dat er niets meer aan te doen is, 422 dat de mailserver heeft
geweigerd en opnieuw proberen kan helpen, 503 dat de controle niet kon worden
gedaan en er niets is geprobeerd — de enige fout hier waarvan gezegd kan
worden dat er niets is veranderd. Je hoeft de lijst niet opnieuw te lezen om uit
te zoeken welke fout je kreeg.
De lijst is om dezelfde reden geordend op wat het eerst verloopt: herstel in die volgorde.
restore_failed is het opnieuw proberen waard, en dekt het definitieve geval niet meer
503 restore_unavailable dekt alleen de voorafgaande lees — die waarmee
wordt vastgesteld of de mailserver het account nog vasthoudt. Een
transportfout later in het herstel, nadat die beslissing is genomen, komt nog
steeds naar buiten als 422 restore_failed; die code bewijst dus niet dat de
mailserver nee zei. Het herstel is idempotent, en daarom is opnieuw proberen de
juiste zet bij alles wat tijdelijk lijkt.
Wat restore_failed niet meer dekt, is het ene geval waarin opnieuw proberen
nooit kan werken: een account dat de mailserver al heeft gewist antwoordt in
plaats daarvan 409 not_recoverable. Je hoeft de lijst dus niet meer opnieuw
te lezen om een 422 die het opnieuw proberen waard is te onderscheiden van een
definitieve.
download_unavailable bij
Een gearchiveerd bericht downloaden
blijft een uitzondering op de 503-conventie die
Leesfouten beschrijft — die code draagt beide
statussen: 404 voor een bericht dat volgens de mailserver weg is, 503 voor
een lees die hij niet kon uitvoeren.
restore_failed dekt ook een herstel dat de mailserver of het pakket weigert:
- het adres is opnieuw in gebruik door een nieuwe mailbox — verwijder of hernoem die eerst en herstel deze daarna;
- de mailboxlimiet van het pakket is al vol — verwijder eerst een andere mailbox of upgrade;
- elke andere weigering van de mailserver.
curl -X PATCH \
-H "Authorization: Bearer $CLOUDPRESS_TOKEN" \
-H "X-Auth-Account: $ACCOUNT_ID" \
-H "Content-Type: application/json" \
-d '{"password": "a-strong-password"}' \
https://my.cloudpress.com/api/mailspace/$MAILSPACE_ID/purged_mailboxes/$PURGED_ID
Fouten
- 404
unknown_purged_mailbox| zo'n record bestaat niet in deze mailomgeving, of het is al verbruikt - 409
not_recoverable| definitief — niet opnieuw proberen. Of het herstelvenster is gesloten, of de voorafgaande lees is gelukt en meldde dat de mailserver het account niet meer vasthoudt. Op dat tweede pad wordt het record onderweg verbruikt, dus een nieuwe poging antwoordt404 - 400
password_blank| geen wachtwoord meegegeven - 422
restore_failed| de mailserver heeft geantwoord en geweigerd — zie hierboven. Het opnieuw proberen waard - 503
restore_unavailable| de voorafgaande lees van de lijst met wistaken van de mailserver kon niet worden uitgevoerd, dus er is niets geprobeerd. Het record is onaangeraakt — probeer het opnieuw - plus de gedeelde controles
Foutcodes
Alle fouten behalve de OAuth-scopefout gebruiken de standaardenvelop
{"errors": [...], "code": "..."} die is beschreven in
Foutreacties. Een onbekende of buiten je bereik vallende
:mailspace_id is de uitzondering in de andere richting: 404 met een lege body en
helemaal geen envelop. Bij
Een gearchiveerd bericht downloaden zijn de
fouten JSON, ook al is een succes dat niet.
Gedeeld door elk endpoint op deze pagina — zie Gedeelde controles:
| Code | Status | Opgeworpen bij |
|---|---|---|
stalwart_unavailable |
503 | alle verzoeken |
not_authorized |
403 | POST, PATCH, DELETE |
mailspace_suspended |
403 | alle verzoeken |
pending_delete |
403 | POST, PATCH, DELETE |
not_provisioned |
409 | alle verzoeken |
Per endpoint:
| Code | Status | Opgeworpen door |
|---|---|---|
archived_items_unavailable |
503 | gearchiveerde mail opvragen |
purged_mailboxes_unavailable |
503 | herstelbare mailboxen opvragen |
unknown_archived_item |
404 | een gearchiveerd item bekijken, terugzetten, downloaden, verwijderen; ook een terugzetting of verwijdering waarvan het item tussen de twee id-lookups verdwijnt |
archived_item_lookup_unavailable |
503 | een gearchiveerd item bekijken, terugzetten, downloaden, verwijderen — de eigendomscontrole kon niet worden uitgevoerd, dus er is niets verstuurd |
restore_failed |
422 | een gearchiveerd item terugzetten; een definitief verwijderde mailbox herstellen |
restore_unconfirmed |
503 | een gearchiveerd item terugzetten — de opdracht is verstuurd en de uitkomst is onbekend |
restore_unavailable |
503 | een definitief verwijderde mailbox herstellen — er is niets geprobeerd |
download_unavailable |
404 / 503 | een gearchiveerd bericht downloaden — 404 het bericht is weg (definitief), 503 elke andere leesfout (opnieuw te proberen) |
delete_failed |
422 | een gearchiveerd item verwijderen |
delete_unconfirmed |
503 | een gearchiveerd item verwijderen — de wissing is verstuurd en kan al zijn uitgevoerd. Onomkeerbaar |
unknown_purged_mailbox |
404 | een definitief verwijderde mailbox herstellen |
not_recoverable |
409 | een definitief verwijderde mailbox herstellen |
password_blank |
400 | een definitief verwijderde mailbox herstellen |
Het endpoint voor het bezorglogboek heeft geen eigen foutcodes — alleen de gedeelde controles.