跳到內容

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_endpoints
  • arinova.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.

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 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 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 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.

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 endpoints are active immediately. An authenticated external Agent uses:

  • GET /api/v1/deliveries
  • GET /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.

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.

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