Skip to Content
API referenceOverview

API overview

The Local Letter API is an Express service you host yourself. Everything below is relative to your own deployment — http://localhost:4000 in development.

Two audiences, two auth schemes

The API is split down the middle by who is calling.

Dashboard routesSDK routes
Paths/projects, /library, /me/v1/*
AuthSession cookie, from signing inAuthorization: Bearer <api key>
CallersThe dashboard, in a browserYour backend, via an SDK
Can doEverything: create, edit, deleteRender templates, verify the key

An API key can only reach /v1. It cannot list, create or modify anything. That’s why a leaked key can render your templates but can’t change them — see API keys.

Response envelope

Every response, success or failure, is a JSON object with the same top-level shape.

Success:

{ "success": true, "statusCode": 200, "message": "Template rendered successfully", "data": { } }

Failure:

{ "success": false, "statusCode": 404, "message": "Template not found" }

Check success rather than sniffing for fields — a 4xx still returns valid JSON in this shape, and the SDKs unwrap data for you. Errors may carry an extra errors field with detail.

Pagination

List endpoints accept ?page= and ?limit=:

ParameterDefaultMaxNotes
page11-based.
limit50100Values above 100 are clamped, not rejected.

Anything unparseable — ?page=abc, ?limit=-5 — silently falls back to the default rather than erroring.

Paginated responses attach meta.pagination:

{ "success": true, "statusCode": 200, "message": "Templates fetched successfully", "data": [], "meta": { "pagination": { "page": 1, "limit": 50, "total": 12, "totalPages": 1 } } }

Status codes

CodeMeaning
200Fine.
201Created — new project, template, locale, API key, or library import.
400Bad input. A required field is missing, or a key/slug/locale doesn’t match its pattern.
401No session, or a missing/invalid API key.
403Valid API key that isn’t linked to a project.
404Not found — or found but not yours.
409Conflict: a slug, template key or locale is already taken.
500Unhandled server error.

Resources you don’t own return 404, not 403. Project lookups are scoped by (slug, ownerId), so someone else’s project is indistinguishable from one that doesn’t exist.

Unauthenticated routes

GET /health is the only endpoint that needs no credentials:

curl http://localhost:4000/health
{ "success": true, "statusCode": 200, "message": "Service is healthy", "data": { "status": "ok" } }

Point your load balancer or container health check at it.

Endpoints at a glance

SDK — bearer key

GET /v1/whoami POST /v1/render/:key

Dashboard — session

GET /me GET /projects POST /projects GET /projects/:slug GET /projects/:slug/templates POST /projects/:slug/templates POST /projects/:slug/templates/import GET /projects/:slug/templates/:key PUT /projects/:slug/templates/:key DELETE /projects/:slug/templates/:key POST /projects/:slug/templates/:key/locales PUT /projects/:slug/templates/:key/locales/:locale DELETE /projects/:slug/templates/:key/locales/:locale GET /projects/:slug/api-keys POST /projects/:slug/api-keys DELETE /projects/:slug/api-keys/:id GET /library/packs GET /library/packs/:packId GET /library/packs/:packId/templates/:templateKey
Last updated on