REST API reference

All paths are relative to https://shopify.workflow-transactional-email.app. Every endpoint except the index needs Authorization: Bearer fak_... - see Authentication and API keys.

Responses are JSON. Errors use the same shape throughout:

json
{ "error": "Invalid or missing API key" }

Index and identity

Method Path Level Purpose
GET /api/v1 none API index. Confirms the API is up and reports the current release
GET /api/v1/me read Check a key works and see its access level

The index needs no key, so it is a safe health check to point a monitor at.

Email layouts

Method Path Level Purpose
GET /api/v1/email-templates read List email layouts
GET /api/v1/email-templates/:id read Get one layout, including its subject, preview text and rendered body

Accepts page (default 1), limit (default 25, max 100) and q to search by name.

Read-only. Create and edit layouts in the app.

Email senders

Method Path Level Purpose
GET /api/v1/smtp-configs read List SMTP senders
GET /api/v1/senders read List connected mailboxes (Microsoft 365, Google)

SMTP senders report hasUsername and hasPassword rather than the credential itself - both halves are withheld, since a username is as much a part of the credential as the password. A sender stays identifiable by its name, host and from-address. Connected mailboxes report a partially masked address such as so***@example.com and never their sign-in tokens.

Read-only. Connect and edit senders in the app.

Secrets

Method Path Level Purpose
GET /api/v1/secrets read List secret names and descriptions

Returns hasValue, never the value. There is no endpoint that reads a secret back. Create and update secrets in the app.

Uploaded files

Method Path Level Purpose
GET /api/v1/files read List uploaded logos, images and attachments
DELETE /api/v1/files write Delete one, by key in the body

List accepts page, limit (max 100) and q to search by filename.

Add ?usage=true and each file also reports inUse plus a usedBy array naming the layouts and presets referencing it. That is how you find everything safe to clear out in a single request. It scans your layouts and presets, so it is opt-in rather than the default.

Uploading is not exposed here - add files through the media picker in the app.

HTTP requests

Method Path Level Purpose
GET /api/v1/http-requests read List HTTP requests
POST /api/v1/http-requests write Create one
GET /api/v1/http-requests/:id read Get one
PUT / PATCH /api/v1/http-requests/:id write Update one
DELETE /api/v1/http-requests/:id write Delete one
POST /api/v1/http-requests/:id/test execute Run it against the real target

List accepts the same page, limit and q parameters as email layouts.

The target url must be http:// or https://. Other schemes are rejected when you save.

Literal credentials written into a header or body are masked in every response. Values referenced as {{ secrets.KEY }} are returned as written, because the reference itself is not sensitive.

History and statistics

Covers both email sends and HTTP requests.

Method Path Level Purpose
GET /api/v1/history read List executions
GET /api/v1/history/:id read Get one execution with request and response data
GET /api/v1/stats read Totals and success rate

History accepts page, limit (max 100), actionType, status (PENDING, SUCCESS, FAILED), and actionConfigId to filter to a single layout or request.

actionType must be exactly EMAIL or HTTP_REQUEST. An unrecognised value is ignored rather than rejected, so a typo returns everything instead of erroring.

Stats accepts days (default 30, max 365).

Status codes

Code Meaning
200 Success
201 Created
400 Malformed request or failed validation
401 Missing, malformed or revoked API key
403 Key is valid but its level is too low for this endpoint
404 Not found, or belongs to a different shop
409 Refused because the record is still in use (file deletion)
429 Rate limited - see below
502 The request ran but the third-party target failed

A record belonging to another shop returns 404 rather than 403, so the API never confirms that an id exists elsewhere.

Rate limits

Two independent budgets, both per key:

  • 300 requests per 60 seconds across all endpoints.
  • 60 execute calls per hour on top of that, covering POST /api/v1/http-requests/:id/test.

Exceeding either returns 429 with an explanatory error. The execute budget is deliberately tight: a runaway loop firing live requests at a payment provider is a much worse failure than a slow script.

Budgets are per key, not per store, so one integration cannot exhaust another's allowance.