Skip to content

Morning review and sending journal ​

Routes are relative to <PUBLIC_BASE_URL>; request and response bodies are JSON unless stated otherwise. Authentication, scopes, pagination and the error format are described in the REST API guides.

GET /api/v1/review ​

List the drafts to review

The member’s queue, cursor-paginated, with the count of drafts truly pending for the menu badge. Filterable by mailbox and workflow.

Access — Member session or API key with scope review:read.

Parameters

NameInTypeRequired
mailboxIdquerystringno
workflowIdquerystringno
cursorquerystringno
limitqueryintegerno

Responses

200 — A page of drafts.

FieldTypeRequired
itemsobject[]yes
nextCursorstring | nullyes
pendingCountintegeryes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "mailboxId": { "type": "string" },
          "mailboxAddress": { "type": "string" },
          "mode": { "type": "string", "enum": [ "draft", "send" ] },
          "origin": { "type": "string", "enum": [ "workflow", "webmail", "approval" ] },
          "recipients": { "type": "array", "items": { "type": "string" } },
          "subject": { "type": "string" },
          "createdAt": { "type": "string" },
          "reviewStatus": {
            "type": "string",
            "enum": [ "none", "pending", "sent", "rejected", "snoozed", "gone" ]
          },
          "reviewReason": { "type": "string" },
          "reviewAt": { "type": "string" },
          "snoozeUntil": { "type": "string" },
          "sendStatus": {
            "type": "string",
            "enum": [
              "pending",
              "held",
              "running",
              "done",
              "failed",
              "cancelled",
              "unknown"
            ]
          },
          "sendErrorKind": { "type": "string" },
          "executionId": { "type": "string" },
          "workflowId": { "type": "string" },
          "workflowName": { "type": "string" },
          "nodeId": { "type": "string" },
          "nodeType": { "type": "string" },
          "messageIdHeader": { "type": "string" },
          "messageId": { "type": "string" },
          "threadId": { "type": "string" }
        },
        "required": [
          "id",
          "mailboxId",
          "mailboxAddress",
          "mode",
          "origin",
          "recipients",
          "subject",
          "createdAt",
          "reviewStatus",
          "sendStatus"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "pendingCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }
  },
  "required": [ "items", "nextCursor", "pendingCount" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — review.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/review/{id} ​

Preview a draft

The only place the body leaves, sanitised. Another member’s draft is a 404.

Access — Member session or API key with scope review:read.

Parameters

NameInTypeRequired
idpathstringyes

Responses

200 — The draft and its body.

FieldTypeRequired
messageobjectyes
ccstring[]yes
bccstring[]yes
bodyTextstringno
bodyHtmlstringno
hasRemoteImagesbooleanyes
attachmentsobject[]yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "message": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "mailboxId": { "type": "string" },
        "mailboxAddress": { "type": "string" },
        "mode": { "type": "string", "enum": [ "draft", "send" ] },
        "origin": { "type": "string", "enum": [ "workflow", "webmail", "approval" ] },
        "recipients": { "type": "array", "items": { "type": "string" } },
        "subject": { "type": "string" },
        "createdAt": { "type": "string" },
        "reviewStatus": {
          "type": "string",
          "enum": [ "none", "pending", "sent", "rejected", "snoozed", "gone" ]
        },
        "reviewReason": { "type": "string" },
        "reviewAt": { "type": "string" },
        "snoozeUntil": { "type": "string" },
        "sendStatus": {
          "type": "string",
          "enum": [
            "pending",
            "held",
            "running",
            "done",
            "failed",
            "cancelled",
            "unknown"
          ]
        },
        "sendErrorKind": { "type": "string" },
        "executionId": { "type": "string" },
        "workflowId": { "type": "string" },
        "workflowName": { "type": "string" },
        "nodeId": { "type": "string" },
        "nodeType": { "type": "string" },
        "messageIdHeader": { "type": "string" },
        "messageId": { "type": "string" },
        "threadId": { "type": "string" }
      },
      "required": [
        "id",
        "mailboxId",
        "mailboxAddress",
        "mode",
        "origin",
        "recipients",
        "subject",
        "createdAt",
        "reviewStatus",
        "sendStatus"
      ],
      "additionalProperties": false
    },
    "cc": { "type": "array", "items": { "type": "string" } },
    "bcc": { "type": "array", "items": { "type": "string" } },
    "bodyText": { "type": "string" },
    "bodyHtml": { "type": "string" },
    "hasRemoteImages": { "type": "boolean" },
    "attachments": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "filename": { "type": "string" },
          "mime": { "type": "string" },
          "size": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }
        },
        "required": [ "filename", "mime", "size" ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "message", "cc", "bcc", "hasRemoteImages", "attachments" ],
  "additionalProperties": false
}

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — review.not_found, review.content_gone. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/review/{id}/send ​

Send a reviewed draft

Optionally with corrections. 202: the send is recorded, not delivered; nothing leaves synchronously. A draft already sent or rejected is a 409.

Access — Member session or API key with scope review:write.

Parameters

NameInTypeRequired
idpathstringyes

Request body (application/json)

FieldTypeRequired
clientTokenstringyes
subjectstringno
bodyTextstringno
bodyHtmlstringno
tostring[]no
ccstring[]no
bccstring[]no
JSON Schema
json
{
  "type": "object",
  "properties": {
    "clientToken": { "type": "string", "minLength": 8, "maxLength": 200 },
    "subject": { "type": "string", "maxLength": 500 },
    "bodyText": { "type": "string", "maxLength": 200000 },
    "bodyHtml": { "type": "string", "maxLength": 400000 },
    "to": { "maxItems": 100, "type": "array", "items": { "type": "string" } },
    "cc": { "maxItems": 100, "type": "array", "items": { "type": "string" } },
    "bcc": { "maxItems": 100, "type": "array", "items": { "type": "string" } }
  },
  "required": [ "clientToken" ]
}

Responses

202 — The message, queued.

FieldTypeRequired
messageobjectyes
opIdstringno
JSON Schema
json
{
  "type": "object",
  "properties": {
    "message": {
      "type": "object",
      "properties": {
        "id": { "type": "string" },
        "mailboxId": { "type": "string" },
        "mailboxAddress": { "type": "string" },
        "mode": { "type": "string", "enum": [ "draft", "send" ] },
        "origin": { "type": "string", "enum": [ "workflow", "webmail", "approval" ] },
        "recipients": { "type": "array", "items": { "type": "string" } },
        "subject": { "type": "string" },
        "createdAt": { "type": "string" },
        "reviewStatus": {
          "type": "string",
          "enum": [ "none", "pending", "sent", "rejected", "snoozed", "gone" ]
        },
        "reviewReason": { "type": "string" },
        "reviewAt": { "type": "string" },
        "snoozeUntil": { "type": "string" },
        "sendStatus": {
          "type": "string",
          "enum": [
            "pending",
            "held",
            "running",
            "done",
            "failed",
            "cancelled",
            "unknown"
          ]
        },
        "sendErrorKind": { "type": "string" },
        "executionId": { "type": "string" },
        "workflowId": { "type": "string" },
        "workflowName": { "type": "string" },
        "nodeId": { "type": "string" },
        "nodeType": { "type": "string" },
        "messageIdHeader": { "type": "string" },
        "messageId": { "type": "string" },
        "threadId": { "type": "string" }
      },
      "required": [
        "id",
        "mailboxId",
        "mailboxAddress",
        "mode",
        "origin",
        "recipients",
        "subject",
        "createdAt",
        "reviewStatus",
        "sendStatus"
      ],
      "additionalProperties": false
    },
    "opId": { "type": "string" }
  },
  "required": [ "message" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.

Error codes — review.bad_request, review.not_found, review.already_resolved, review.content_gone. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/review/send ​

Send a selection of drafts

Up to fifty ids. 202, with a verdict per draft: one that cannot be sent does not block the others.

Access — Member session or API key with scope review:write.

Request body (application/json)

FieldTypeRequired
idsstring[]yes
clientTokenstringyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "ids": {
      "minItems": 1,
      "maxItems": 50,
      "type": "array",
      "items": { "type": "string" }
    },
    "clientToken": { "type": "string", "minLength": 8, "maxLength": 200 }
  },
  "required": [ "ids", "clientToken" ]
}

Responses

202 — The verdicts.

FieldTypeRequired
sentintegeryes
skippedobject[]yes
pendingCountintegeryes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "sent": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
    "skipped": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": { "id": { "type": "string" }, "reason": { "type": "string" } },
        "required": [ "id", "reason" ],
        "additionalProperties": false
      }
    },
    "pendingCount": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 }
  },
  "required": [ "sent", "skipped", "pendingCount" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.

Error codes — review.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/review/{id}/reject ​

Reject a draft

With an optional reason, kept in the sending journal.

Access — Member session or API key with scope review:write.

Parameters

NameInTypeRequired
idpathstringyes

Request body (application/json)

FieldTypeRequired
reasonstringno
JSON Schema
json
{
  "type": "object",
  "properties": { "reason": { "type": "string", "maxLength": 500 } }
}

Responses

200 — The message, rejected.

FieldTypeRequired
messageobjectyes
opIdstringno

Same schema as POST /api/v1/review/{id}/send.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — review.bad_request, review.not_found, review.already_resolved. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

POST /api/v1/review/{id}/snooze ​

Snooze a draft

The draft leaves the queue until until, then comes back.

Access — Member session or API key with scope review:write.

Parameters

NameInTypeRequired
idpathstringyes

Request body (application/json)

FieldTypeRequired
untilstring (date-time)yes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "until": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
    }
  },
  "required": [ "until" ]
}

Responses

200 — The message, snoozed.

FieldTypeRequired
messageobjectyes
opIdstringno

Same schema as POST /api/v1/review/{id}/send.

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — review.bad_request, review.not_found, review.already_resolved. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/sending/journal ​

Read the sending journal

Every outbound message of the member (workflows, webmail, approvals), cursor-paginated. Filters: mailbox, workflow, mode, status, text search on subject and recipients, date range.

Access — Member session or API key with scope review:read.

Parameters

NameInTypeRequired
mailboxIdquerystringno
workflowIdquerystringno
modequery"draft" | "send"no
statusquery"none" | "pending" | "sent" | "rejected" | "snoozed" | "gone"no
qquerystringno
sincequerystring (date-time)no
untilquerystring (date-time)no
cursorquerystringno
limitqueryintegerno

Responses

200 — A page of the journal.

FieldTypeRequired
itemsobject[]yes
nextCursorstring | nullyes
JSON Schema
json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "mailboxId": { "type": "string" },
          "mailboxAddress": { "type": "string" },
          "mode": { "type": "string", "enum": [ "draft", "send" ] },
          "origin": { "type": "string", "enum": [ "workflow", "webmail", "approval" ] },
          "recipients": { "type": "array", "items": { "type": "string" } },
          "subject": { "type": "string" },
          "createdAt": { "type": "string" },
          "reviewStatus": {
            "type": "string",
            "enum": [ "none", "pending", "sent", "rejected", "snoozed", "gone" ]
          },
          "reviewReason": { "type": "string" },
          "reviewAt": { "type": "string" },
          "snoozeUntil": { "type": "string" },
          "sendStatus": {
            "type": "string",
            "enum": [
              "pending",
              "held",
              "running",
              "done",
              "failed",
              "cancelled",
              "unknown"
            ]
          },
          "sendErrorKind": { "type": "string" },
          "executionId": { "type": "string" },
          "workflowId": { "type": "string" },
          "workflowName": { "type": "string" },
          "nodeId": { "type": "string" },
          "nodeType": { "type": "string" },
          "messageIdHeader": { "type": "string" },
          "messageId": { "type": "string" },
          "threadId": { "type": "string" }
        },
        "required": [
          "id",
          "mailboxId",
          "mailboxAddress",
          "mode",
          "origin",
          "recipients",
          "subject",
          "createdAt",
          "reviewStatus",
          "sendStatus"
        ],
        "additionalProperties": false
      }
    },
    "nextCursor": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
  },
  "required": [ "items", "nextCursor" ],
  "additionalProperties": false
}

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — review.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.

GET /api/v1/sending/journal.csv ​

Export the sending journal as CSV

The same filters, without cursor or limit: the whole filtered result, streamed and capped at fifty thousand rows. Served as an attachment.

Access — Member session or API key with scope review:read.

Parameters

NameInTypeRequired
mailboxIdquerystringno
workflowIdquerystringno
modequery"draft" | "send"no
statusquery"none" | "pending" | "sent" | "rejected" | "snoozed" | "gone"no
qquerystringno
sincequerystring (date-time)no
untilquerystring (date-time)no

Responses

200 — The CSV file.

Content type : text/csv

400 — The request does not match its schema.

401 — No valid session or API key (auth.unauthenticated).

403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).

429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.

Error codes — review.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.