Sūti vēstules no savas sistēmas
Viens HTTP pieprasījums ar tekstu un adresi, un izdrukāta, apmaksāta vēstule nonāk pastkastītē. Bez printera, bez pastmarkām, bez gājiena līdz pastkastei.
Izveidot atslēguVisa integrācija
Tas ir viss. Atbildē ir identifikators, ar kuru nolasi statusu, un tas pats identifikators parādās arī rēķina rindā.
curl -X POST https://sendletter.eu/api/v1/letters \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"text": "Geachte heer, mevrouw,\n\nHierbij zeg ik mijn abonnement op.",
"subject": "Opzegging",
"sender": { "name": "Jan de Vries", "street": "Merelstraat", "number": "64",
"postalCode": "8916 AX", "city": "Leeuwarden", "country": "NL" },
"recipient": { "company": "Voorbeeld BV", "name": "Afdeling Klantenservice",
"street": "Hoofdstraat", "number": "1",
"postalCode": "1011 AA", "city": "Amsterdam", "country": "NL" },
"product": "standard",
"idempotencyKey": "opzegging-2026-07-28"
}'const res = await fetch("https://sendletter.eu/api/v1/letters", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SENDLETTER_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ text, sender, recipient, product: "registered",
// Optional. Bills a company you belong to instead of
// this key's own wallet; leave it out for a personal
// letter. One you may not spend is refused, not billed
// to you.
organizationId: company.id,
idempotencyKey: invoice.id }),
})
const letter = await res.json()
// letter.id is what you store; poll it or wait for the webhook.
// letter.billedToOrganizationId says which wallet actually paid.$ch = curl_init("https://sendletter.eu/api/v1/letters");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("SENDLETTER_KEY"),
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"text" => $body, "sender" => $sender, "recipient" => $recipient,
"idempotencyKey" => $invoiceId,
]),
]);
$letter = json_decode(curl_exec($ch), true);import os, requests
letter = requests.post(
"https://sendletter.eu/api/v1/letters",
headers={"Authorization": f"Bearer {os.environ['SENDLETTER_KEY']}"},
json={"text": body, "sender": sender, "recipient": recipient,
"idempotencyKey": invoice_id},
timeout=30,
).json()Atslēgas
Katrā pieprasījumā ir API atslēga kā bearer marķieris. Atslēgas izveido savā kontā un katru no tām redzi tieši vienu reizi; mēs glabājam tikai jaucējvērtību. Atslēgas atsaukšana stājas spēkā nekavējoties.
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxPārbaudi, pirms vēstule aiziet pastā
Atslēga, kas sākas ar sk_test_, aprēķina cenu un sagatavo vēstuli tieši tāpat kā īstā, tikai apstājas tieši pirms printera. Tā vari izmēģināt visu integrāciju, un neviena vēstule neaiziet.
Galapunkti
| POST | /api/v1/letters | Send a letter |
| GET | /api/v1/letters | List letters, newest first |
| GET | /api/v1/letters/{id} | One letter, with its timeline |
| POST | /api/quote | Price, and what that country can be sent(no key) |
| GET | /api/countries | Destinations and their address rules(no key) |
| GET | /api/v1/openapi.json | The machine readable spec(no key) |
Ne katram galamērķim ir pieejami visi produkti. Vaicā /api/quote un nolasi availableProducts: uz Nīderlandi, piemēram, ierakstītas vēstules pakalpojuma nav. Tā vari attiecīgo iespēju izslēgt, nevis ļaut tai neizdoties nosūtīšanas brīdī.
41 destinations. Registered mail: DE, BE, FR, ES, CH.
Statuses
- queued
- Accepted and waiting on the print partner.
- processing
- An exclusive hand-off to the print partner is in progress.
- attention_required
- The provider result is uncertain; held for reconciliation and never retried automatically.
- submitted
- Handed over to the printer.
- printed
- Printed and enveloped.
- posted
- Handed to the carrier. A response deadline runs from here.
- delivered
- Confirmed delivered. Registered mail only.
- failed
- Not sent. `statusDetail` says why, and the payment is returned.
Statusa paziņojumi tavām sistēmām
Vēstule dažu dienu laikā maina stāvokli pāris reižu. Regulāra aptaujāšana tam neder: vai nu jautā daudz par biežu, vai uzzini daudz par vēlu. Reģistrē galapunktu savā kontā, un mēs paziņosim paši.
POST your-endpoint
X-SendLetter-Event: letter.posted
X-SendLetter-Signature: 9f86d081...
{ "type": "letter.posted",
"sentAt": "2026-07-28T18:30:00.000Z",
"letter": { "id": "AbC123", "status": "posted", "trackingCode": null, ... } }Katrs paziņojums ir parakstīts. Aprēķini HMAC-SHA256 no neapstrādātā pieprasījuma ķermeņa ar savu noslēpumu un salīdzini to ar galvenes X-SendLetter-Signature vērtību. Dari to, pirms ķermeni parsē: parsēšana un atkārtota serializācija maina baitus, un paraksts vairs nekad nesakritīs.
import crypto from "node:crypto"
// The raw body, before any JSON parsing.
const expected = crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(rawBody, "utf8").digest("hex")
const sent = req.headers["x-sendletter-signature"]
if (expected.length !== sent.length ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sent))) {
return res.status(401).end()
}Kļūdas
Visām kļūdām ir viena forma: kods, pēc kura zarot loģiku, un ziņojums, ko izlasīt. Zaro pēc koda, jo ziņojums var mainīties.
{ "error": { "code": "no_route",
"message": "Registered mail has no route to NL",
"details": null },
"requestId": "req_018f38d7c9f34738adbe1f06a9b36f11" }Every response also carries X-Request-Id. Include that value when asking support about one call; use the letter id to reconcile the complete order.
| 401 | unauthorised | No key, a revoked key, or a malformed header. |
| 400 | invalid_request | The body failed validation. `details` names the fields. |
| 402 | insufficient_balance | The wallet is short. `details` carries required and available. |
| 403 | insufficient_scope / credential_spending_limit | The key lacks a scope or its per-letter ceiling is too low. Nothing is charged. |
| 429 | rate_limited / credential_spending_limit | A request, daily-spend or monthly-spend ceiling was reached. |
| 403 | organization_forbidden | The named organisation exists and this account is in it, but its role may not spend there. Nothing is charged, to it or to you. |
| 404 | organization_not_found | No organisation of that id that this account belongs to. Refused rather than billed to your own wallet. |
| 409 | no_route | That product is not sold to that country. Ask /api/quote first. |
| 413 | too_many_pages | Over the sheet limit for that destination. |
| 404 | not_found | No such letter on this account. |
OpenAPI
Pilnu specifikāciju izsniedzam tiešsaistē, tāpēc tā nekad nevar aprakstīt versiju, kas nedarbojas. Pavērs uz to ģeneratoru, un tev ir klients tavā programmēšanas valodā.