Registered Outbound Delivery
本頁內容尚未翻譯。
Outbound Delivery sends an event and JSON payload to an owner-registered external endpoint without exposing a general-purpose HTTP tool. Owners manage endpoints and grants in Office > Outgoing Deliveries. The Agent surface is:
arinova.delivery.list_endpointsarinova.delivery.send { endpoint_id, event, payload }
List returns only explicitly granted safe metadata; send requires an explicit
endpoint write grant. Endpoint creation, Push verification, Custom activation,
request templates, secret slots, disablement, and deletion remain owner Office
operations.
Agent authorization
Section titled “Agent authorization”Endpoint grants are per Agent. read permits safe listing and Pull access;
write also permits sending and authoring a Workflow graph that references the
endpoint. Missing, unauthorized, and cross-owner resources are deliberately
indistinguishable to the Agent.
The migration granted existing endpoints only to active external Agents. Platform Agents received zero grants, and new endpoints are not shared automatically. An empty Action list may therefore mean that the owner has not granted this Agent any endpoint, not that the owner has no endpoint.
Push verification and signing
Section titled “Push verification and signing”Push endpoints must use public HTTPS and start pending_verification. Office
starts a 15-minute challenge. Arinova POSTs:
{"type":"verification","challenge":"ovc_..."}The receiver returns the exact token as plain text or JSON. URL or signing-secret changes return the endpoint to pending verification and terminalize queued or in-flight work. Redirects are not followed.
Each Push delivery uses the stable envelope:
{ "id": "delivery UUID", "event": "order.completed", "createdAt": "RFC 3339 timestamp", "payload": {}}The request includes X-Arinova-Delivery-Id, X-Arinova-Event, and
X-Arinova-Signature: t=<unix>,v1=<hex>. Verify HMAC-SHA256 over the exact
bytes "{t}.{request_body}" in constant time. The signing secret is
display-once and cannot be read by an Agent. Rotation invalidates the previous
secret immediately.
An Official account may bind an owner-matching active verified Push endpoint. URL/secret changes, re-verification, disablement, and binding changes fence captured Official automation runs. The binding never grants Agent access. Official cannot bind Pull or Custom endpoints.
Custom HTTP targets
Section titled “Custom HTTP targets”Custom endpoints let the owner define a restricted POST, PUT, or PATCH
request. The owner controls the URL template, HTTP method, header templates,
JSON body template, and named secrets; an Agent still supplies only event and
payload through the unchanged Delivery Actions. The rendered JSON body is sent
directly, without the Push envelope, X-Arinova-Event, or HMAC signature.
Arinova retains its User-Agent, Content-Type: application/json, and
X-Arinova-Delivery-Id headers.
Templates and limits
Section titled “Templates and limits”Templates intentionally support only {{payload.<dotted.path>}} and
{{secret.<name>}}. Rendering is one pass, so directive-looking text supplied
inside a payload is not evaluated again. A missing payload path is a terminal
rendering error. Office stores and displays only the placeholder templates;
rendered URLs, headers, bodies, and secret values are never persisted or logged.
- The URL must have a literal public HTTPS origin. Directives are allowed only in path segments and query values, are percent-encoded with a fixed policy, and cannot change the literal host or port. The rendered URL is checked again for SSRF before dispatch, and redirects are rejected.
- Header names cannot contain directives, and fixed or unsafe transport headers cannot be overridden. Header values reject CR, LF, and NUL.
- The body template must be valid JSON. A string node containing only one
payload directive preserves a string, number, boolean, or null value by
default. Objects and arrays require the explicit whole-value form
{{payload.path|object}}; that modifier is invalid in URLs, headers, interpolated strings, and secret directives. JSON object keys cannot contain directives. - The URL is at most 2,048 characters after rendering. An endpoint may define at most 20 headers; names are at most 128 characters and template values at most 2,048. The JSON body template is at most 16,384 characters, the rendered body is at most 64 KiB, and the entire request template may contain at most 64 directives.
The template must reference at least one secret, and every referenced slot must
be set before activation. Slot names start with an ASCII letter, then contain
only letters, digits, or underscores, up to 32 characters. Values contain
12–4,096 characters and are encrypted at rest. Values are never returned;
Office shows only a last-four-character hint. Adding, rotating, or deleting a
slot clears prior activation and terminalizes queued work as
endpoint_secret_rotated; a referenced slot cannot be deleted until the
template stops using it.
Activation and configuration changes
Section titled “Activation and configuration changes”A Custom endpoint starts pending_verification. The owner activates it by
sending a required sample payload through the real transport and receiving a
2xx response. The sample is not stored. A hashed 15-minute activation token
prevents a concurrent URL, template, or secret change from committing an older
test result. Activation attempts are limited in Redis to six per rolling hour
per owner and fail closed when the limiter is unavailable.
Activation proves only that the target accepted an Arinova request and lets the owner observe the actual request. Requiring a secret adds friction; neither condition proves authorization to, or ownership of, the destination. The real abuse boundaries remain owner authentication, endpoint and enqueue quotas, SSRF validation, and the no-redirect policy.
A URL change returns the endpoint to pending_verification, clears activation,
and terminalizes queued work as endpoint_url_changed. Any secret-slot
addition, rotation, or deletion does the same with
endpoint_secret_rotated. Changing only the method, headers, or body keeps an
active endpoint active, but clears any in-progress activation token and
terminalizes already queued work as endpoint_template_changed; newly queued
work uses the new template.
For chat-style providers, prefer one endpoint whose body uses a routing value
such as {{payload.chatId}}; this lets one reviewed template serve many chats.
Creating one endpoint per chat is supported as a secondary isolation model, but
the owner-wide limit of ten endpoints makes it unsuitable for broad fan-out.
Pull API
Section titled “Pull API”Pull endpoints are active immediately. An authenticated external Agent uses:
GET /api/v1/deliveriesGET /api/v1/deliveries/{delivery_id}POST /api/v1/deliveries/{delivery_id}/ack
List, get, and idempotent ack all require that acting Agent’s endpoint read
or write grant in addition to owner scope. Ungranted rows look not found.
Pagination uses an opaque cursor, and the dedicated limiter fails closed at
120 requests per 60 seconds per Agent.
Workflow Delivery steps
Section titled “Workflow Delivery steps”A Workflow graph may contain:
{ "id": "publish-result", "type": "delivery", "endpointId": "endpoint UUID", "event": "workflow.step", "content": "{{steps.summarize.output}}", "next": null}event is optional and defaults to workflow.step. Execution enqueues the
exact JSON value {workflowId,runId,stepName,content} and completes the step at
queued, not at receiver acceptance. A Custom endpoint evaluates its body,
URL, and header templates against that same four-key value as its payload;
the Custom template does not change the Workflow or direct Action payload
shape.
When an Agent creates or updates such a graph, graph validation requires that
same Agent’s endpoint write grant. Owner-authored graph writes are unaffected.
Activation deliberately does not retroactively revalidate saved graphs after
a grant change; runtime dispatch remains owner-scoped.
Direct sends and Workflow Delivery steps share endpoint 60/minute and owner
1,000/UTC-day quotas and charge the owner at enqueue. Failure fails the
Workflow run; each loop visit can enqueue and charge independently. A
notify step is different: it writes an internal Notification Center message,
never sends externally, and never fails the run.
Reliability, limits, billing, and debugging
Section titled “Reliability, limits, billing, and debugging”queued means accepted by Arinova, not received. For Push and Custom HTTP
deliveries, timeout, connection failure, HTTP 5xx, and HTTP 429 are transient.
Retry-After on 429 can only lengthen the normal retry ladder and is capped at
one hour; a 429 still consumes an attempt. Other HTTP 4xx and all redirects are
terminal. Both kinds are capped at five attempts. HTTP 410 disables the
endpoint. Twenty consecutive non-429 failures set auto_disabled; 429 has a
separate consecutive counter and also auto-disables at twenty. Response bodies
are never read.
The serialized service payload limit is 256 KiB. The 64 KiB argument envelope
applies only to direct arinova.delivery.send; Workflow steps do not traverse
that Action. The Custom rendered-body limit is independently 64 KiB. Redis
quota failures fail closed. Enqueue charges once, retries do not charge again,
and a later failed or dead status does not automatically refund.
Cron source
Section titled “Cron source”Platform Cron can queue a delivery directly with the outbound_delivery
target when no Workflow steps are needed. The target stores an owned active
endpointId, a validated event, and an optional JSON bodyTemplate. Each
fire revalidates endpoint ownership/status, renders against the scheduled Cron
context, and enforces the normal 256 KiB service payload limit before calling
the same enqueue and SSRF-safe worker boundary used by direct and Workflow
delivery.
The Cron fire-event UUID is persisted as source_fire_event_id. Enqueue first
acquires a transaction advisory lock derived from that UUID, then rechecks for
an existing committed receipt. A replay returns that receipt before touching
the Redis rate limiter or wallet, and a partial unique index is the durable
backstop. This ordering guarantees one delivery row and one debit even when two
same-source requests race. A disabled, deleted, or foreign endpoint becomes a
visible Cron skip; insufficient credit skips with a Wallet destination. Rate
limit, Redis, wallet-infrastructure, and database outcomes preserve the normal
Delivery error mapping and retry the same Cron occurrence where required.
Terminal rows remain for 30 days, but Delivery history intentionally stores no
payload, rendered URL, or rendered body. Workflow run detail stores only
contentBytes, not rendered content. Neither UI can recover the sent content
later; inspect typed, redacted errors and endpoint history before retrying.
Build a7f47a5ca54ddcf7806cd48b81ce1b9827042766