POST /v1/observe/actions
The Observe Mode relay. It is deliberately separate from POST /v1/actions and the rest of the governed pipeline: this endpoint runs only for trial agents on an ephemeral, unclaimed organisation, relays a call to a vetted platform using a credential you supply per call, and writes one metadata-only audit row. See Observe Mode for the concept.
This route applies no policy. It doesn’t check scopes and it can’t hold a call for approval: it either relays or fails closed on a precondition. If you need governance (scopes, approval, revocation), use
/v1/actionson a real agent.
Request
POST /v1/observe/actions HTTP/1.1
Host: api.agentvalet.ai
Authorization: Bearer <trial-agent-rs256-jwt>
X-AV-Observe-Credential: <your-platform-api-key>
Content-Type: application/json{
"platform": "github",
"endpoint": "/user",
"method": "GET",
"action": "github.user.read"
}| Field | Type | Required | Notes |
|---|---|---|---|
platform | string | yes | Platform id from the catalog (e.g. github, slack, stripe). Must have a vetted observe_base_url; not every platform is relayable |
endpoint | string | yes | Path relative to the platform’s vetted base URL. Absolute URLs, protocol-relative URLs, and .. path traversal are rejected |
method | string | yes | GET / POST / PUT / PATCH / DELETE |
action | string | no | A label for the audit row. Defaults to observe.relay |
body | object | no | Request payload forwarded upstream (ignored for GET) |
headers | object | no | Extra headers forwarded upstream |
The credential itself never goes in the body. It goes on the X-AV-Observe-Credential header, is applied to the upstream Authorization header in memory, and is never written to the audit row, a log, or storage.
Success
The upstream response is relayed back with its original status code and body, unwrapped (there is no discovery envelope, unlike /v1/actions).
Error codes
| Status | error | Cause |
|---|---|---|
| 400 | Missing X-AV-Observe-Credential header | No credential header on the request |
| 400 | platform, endpoint, method are required | Body missing a required field |
| 400 | endpoint_blocked | The endpoint escapes the platform’s vetted base URL, or resolves to a private/link-local/metadata host |
| 400 | unsupported_auth_type | The platform’s catalog auth_type isn’t one Observe Mode knows how to apply (api_key, oauth, basic) |
| 401 | Missing Bearer token / Invalid JWT | No or invalid agent JWT |
| 403 | observe_mode_required | The agent isn’t in observe mode on an ephemeral org; this route refuses to run for any other agent |
| 403 | platform_not_relayable | The platform has no vetted observe_base_url in the catalog |
| 429 | Too many observe calls. Slow down or sign up. | Per-trial rate limit (60 calls/minute) |
| 502 | Upstream request failed | The platform returned an error or the call timed out |
| 503 | Observe Mode is at capacity. Please sign up. | The global cap across all trials is exceeded (OBSERVE_GLOBAL_CAP, default 1000/minute) |
Every one of these outcomes, including the failures above, writes an audit row: denied for the precondition and validation failures, observed for a completed relay (any upstream status, including upstream errors).
Audit
Every call writes one row to audit_log with:
result=observed(relay completed, any upstream status) ordenied(precondition/validation failure)action= youractionfield, orobserve.relayscopes_used=null(Observe Mode does not use scopes)metadata=platform,endpoint,method,action,status,latency_ms,request_size,response_size; no request or response body, and the credential value is redacted wherever it would otherwise appear
Response bodies are capped at 1 MB read from upstream and are not persisted regardless of size. Read Observe Mode for the full retention and containment story.
Next
- Observe Mode: the concept, the containment invariants, and the try flow
POST /v1/actions: the governed action proxy this route intentionally does not touch