Skip to content

delivery.pending v1

A post approved for manual delivery still owes the board its live link (charter: the decision envelope covers questions and follow-ups §3). Pushed when the gate is resolved approve_manual, and read back by the carrier’s sweep from GET /v1/boards/:id/deliveries/pending, from one builder. Mirrors SuperPipeline’s DeliveryPendingBody (apps/api/src/board/views.ts). Readers ignore properties they do not know.

Every event below carries dataschema https://raw.githubusercontent.com/SuperJackfruitLabs/treaty/main/schemas/superpipeline/delivery.pending.v1.json.

Body event CloudEvents type Subject
delivery.pending com.superjackfruit.superpipeline.delivery.pending.v1 evt.<workspace>.superpipeline.delivery.pending

The subject is the NATS subject the relay files it under; <workspace> is the board’s workspace. See the envelope and subjects.

  • Produced by SuperPipeline, through the relay.
  • Consumed by:
    • SuperExchange: Posts it to the board’s room, for the boards it carries. Receives it through the relay.
    • AgentPod: The hub’s board chat posts it, for the boards it still carries.
    • SuperDispatch: Takes it as a nudge to look at the board soon, on a board opted into scheduling. Reads nothing from its data.

Readers ignore properties they do not know, so a field may be added without a new version.

Field Type Required Description Constraints
event "delivery.pending" yes
boardId string or null yes Null only on a board not yet initialised; a carrier needs it to route the event.
boardName string yes Empty string when the board has no name, never absent.
cardId string yes
cardTitle string yes
gateId string yes
stageKey string yes
approvalSubject object yes A subject as a decision envelope carries it (gate.v2 §3): identity, expiry and the kind’s bounded preview. The canonical payload stays behind the authenticated route.
approvalSubject.id string yes
approvalSubject.digest string yes
approvalSubject.schema string yes
approvalSubject.revision integer yes at least 0
approvalSubject.expiresAt string or null yes RFC 3339 with seconds and an explicit offset, or null. The pattern states it for a validator that does not assert format. format date-time; pattern ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?(Z|[+-]([01]\d|2[0-3]):[0-5]\d)$
approvalSubject.preview array of object (type = “text”) or object (type = “field”) or object (type = “link”) yes
approvalSubject.preview[].type "text" yes in the type = "text" shape
approvalSubject.preview[].label string no in the type = "text" shape
approvalSubject.preview[].text string yes in the type = "text" shape
approvalSubject.preview[].type "field" yes in the type = "field" shape
approvalSubject.preview[].label string yes in the type = "field" shape
approvalSubject.preview[].value string yes in the type = "field" shape
approvalSubject.preview[].type "link" yes in the type = "link" shape
approvalSubject.preview[].label string yes in the type = "link" shape
approvalSubject.preview[].url string yes in the type = "link" shape
approvalSubject.previewComplete boolean yes False when the preview was cut to its bounds; no approving option may then be offered.
options array of object yes record_live_url always; switch_to_automatic only when the subject’s preview was complete.
options[].id one of "record_live_url", "switch_to_automatic" yes
options[].label string yes
options[].meaning one of "approve", "reject", "revise", "choose", "defer" yes What choosing the option does (gate.v2 §2).
options[].needsComment false yes
options[].input object no What choosing the option asks the person to type.
options[].input.kind one of "text", "url" yes
options[].input.label string yes
options[].input.required boolean yes
options[].input.maxLength integer yes at least 1
options[].delivery "automatic" no
deadline object no When the decision is reminded about and escalated. Absent when its stage sets no timeout.
deadline.remindAt string no format date-time
deadline.escalateAt string no format date-time
ts string yes When the gate was resolved. format date-time

From fixtures/superpipeline-events/examples.json, case “both-options”:

{
"event": "delivery.pending",
"boardId": "brd_134a0ddb6d434478",
"boardName": "Fixture board",
"cardId": "crd_0f3c9a2b7d4e4a11",
"cardTitle": "Sanitized approval fixture",
"gateId": "gat_5d1e2f3a4b5c6d7e",
"stageKey": "approve",
"approvalSubject": {
"id": "aps_2b7c9d0e1f2a3b4c",
"digest": "sha256:9f2c1a7e5b3d4c6a8e0f1b2d3c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e",
"schema": "social-publish/v1",
"revision": 1,
"expiresAt": "2030-01-02T03:04:05Z",
"preview": [
{
"type": "text",
"label": "Post",
"text": "Shipping the new board view today."
}
],
"previewComplete": true
},
"options": [
{
"id": "record_live_url",
"label": "I posted it — add the link",
"meaning": "defer",
"needsComment": false,
"input": {
"kind": "url",
"label": "Live link",
"required": true,
"maxLength": 2000
}
},
{
"id": "switch_to_automatic",
"label": "Post it automatically instead",
"meaning": "approve",
"needsComment": false,
"delivery": "automatic"
}
],
"ts": "2026-11-10T09:30:00.000Z"
}

The corpus has 2 valid cases in all:

cut-preview-with-deadline
{
"event": "delivery.pending",
"boardId": "brd_134a0ddb6d434478",
"boardName": "Fixture board",
"cardId": "crd_0f3c9a2b7d4e4a11",
"cardTitle": "Sanitized approval fixture",
"gateId": "gat_5d1e2f3a4b5c6d7e",
"stageKey": "approve",
"approvalSubject": {
"id": "aps_2b7c9d0e1f2a3b4c",
"digest": "sha256:9f2c1a7e5b3d4c6a8e0f1b2d3c4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e",
"schema": "social-publish/v1",
"revision": 1,
"expiresAt": null,
"preview": [
{
"type": "text",
"label": "Post",
"text": "Shipping the new board view today."
}
],
"previewComplete": false
},
"options": [
{
"id": "record_live_url",
"label": "I posted it — add the link",
"meaning": "defer",
"needsComment": false,
"input": {
"kind": "url",
"label": "Live link",
"required": true,
"maxLength": 2000
}
}
],
"ts": "2026-11-10T09:30:00.000Z",
"deadline": {
"remindAt": "2026-11-10T10:30:00.000Z",
"escalateAt": "2026-11-10T13:30:00.000Z"
}
}

Each of these must fail. The corpus says why:

  • the event names itself
  • an option is record_live_url or switch_to_automatic
  • a follow-up always carries its subject
  • a subject’s expiry has seconds
  • a subject’s expiry has an offset with a colon