Action Webhooks

Action webhooks let external systems create AgentPress actions by posting JSON events to a configured webhook URL. Accepted deliveries are visible on the unified Actions ledger with their ingress payload, action status, approval state, execution details, and outbound callback results in one row.

For SDK-based sending and callback verification, see Using the SDK.

Endpoint

Send events to the action webhook URL shown in the console:

POST https://api.agent.press/webhooks/actions/{org}/{webhookIdentifier}

{org} can be the organization slug or ID. {webhookIdentifier} is the identifier shown on the webhook detail page.

The older /webhooks/ingest/{org}/{identifier} listener URL is a hidden compatibility path for existing integrations. New SDK calls and manual sender scripts should use /webhooks/actions/{org}/{webhookIdentifier}.

Compatibility Impact

No SDK method was removed, and existing client.webhooks.send() calls continue to work for action webhooks that use the default Svix verification scheme. The client-impacting changes are operational:

  • New action webhook configuration is managed on the action webhook itself, not through a separate listener surface.
  • Existing listener aliases and copied /webhooks/ingest/{org}/{identifier} URLs remain accepted for compatibility, but new integrations should not build against that hidden route.
  • If a sender's webhook is changed from Svix to hmac_sha256, shared_token, or none, SDK callers must pass the matching auth option and manual cURL scripts must send the matching headers.
  • 202 with buffered: true means AgentPress accepted the delivery but has not created an action yet. Do not assume every accepted response includes an actionId.
  • For verificationScheme: "none", the URL is the credential. Creating, switching, or rotating a no-header webhook can change the webhookIdentifier; copy the latest URL after those operations.

Configure a Webhook

In the AgentPress console:

  1. Open Actions.
  2. Click New action webhook to create one, or Manage webhooks to edit existing webhooks.
  3. Choose the action identity, target agent, run-as user, optional event filter, verification scheme, rate limit, schedule, approval rules, and outbound callbacks.
  4. Copy the action webhook URL from the webhook detail page.
  5. Copy the secret shown on create or rotate. Secrets are only shown once.

New action webhooks default to Svix verification. You can switch a webhook to HMAC SHA-256, shared token, or no-header verification from the webhook detail page.

The Send Test dialog uses the webhook's configured verification scheme when it posts to the public webhook URL. If you select an internal AgentPress user in the dialog, the test creates the action directly for that user and does not exercise public webhook signature verification. To test sender authentication end-to-end, either leave the internal user unselected in the dialog or use the SDK/cURL examples below against the copied action webhook URL.

Payload

All action webhooks receive JSON:

interface ActionWebhookPayload {
  eventType: string;
  externalId?: string;
  userId?: string;
  authProvider?: string;
  actionRuleId?: string;
  instructions?: string;
  /** Per-request tool-approval overrides. See "Per-request approval overrides". */
  toolApprovals?: Record<string, "allow" | "ask" | "agent">;
  data?: Record<string, unknown>;
}

Use externalId for idempotency when the sender may retry the same event. If a matching action already exists, AgentPress returns the existing actionId instead of creating a duplicate.

User Resolution

AgentPress resolves the action's run-as user in this order:

  1. userId plus authProvider from the payload, resolved through external auth.
  2. userId as an internal AgentPress user UUID.
  3. The default run-as user configured on the action webhook.

If no user can be resolved, the delivery is accepted into the webhook event log and returns 202 with buffered: true and reason: "user_unresolved". The row appears in the Actions ledger so an operator can fix configuration and replay or let the worker retry.

Per-request approval overrides

Each approval-gated tool the action's agent can call resolves to one of three modes:

  • allow — the tool runs automatically, no human approval.
  • ask — the tool always pauses for human approval before running.
  • agent — the agent decides, per call, whether the action needs approval, following its own prompt/instructions (for example: auto-post 4–5★ replies but ask for approval on 1–3★).

For agent mode, AgentPress augments the tool with a hidden per-call decision the model fills from the agent's prompt policy; a fail-closed predicate enforces it — only an explicit "no approval needed" runs automatically, while anything else (or an unsure agent) pauses for a human:

agent mode needs the agent's prompt to encode the policy — selecting the mode alone is not enough. See the Tool Approvals guide for the full walkthrough.

The default mode for each tool is configured on the action rule in the console (Actions → your webhook → Approval rules). A sender can override a tool's mode per event with toolApprovals:

{
  "eventType": "review.created",
  "data": { "rating": 2 },
  "toolApprovals": { "respondToReview": "ask" }
}

Per-request overrides are only honored when the webhook has Allow per-request overrides enabled (off by default). A user's explicit "always deny" preference always wins over any override. Invalid modes are dropped; unknown tool names are accepted but ignored at runtime (they match no tool).

Verification Schemes

SchemeHeadersNotes
svixsvix-id, svix-timestamp, svix-signatureDefault for new action webhooks and SDK sends.
hmac_sha256x-webhook-timestamp, x-webhook-signatureHMAC-SHA256 over ${timestamp}.${rawBody}.
shared_tokenAuthorization: Bearer <token> or x-webhook-tokenToken is compared against the webhook secret.
nonenoneCapability URL. The unguessable URL is the credential. Use only for intentionally public senders.

When verificationScheme is none, creating, switching, or rotating the webhook mints an unguessable webhook identifier. Manual scripts must use the latest URL copied from the webhook detail page after rotation.

Manual Sender Examples

The SDK is the recommended sender because it handles the default Svix signing flow automatically:

import { AgentPress } from "@agentpress/sdk";

const client = new AgentPress({
  org: "your-org-slug",
  webhookSecret: process.env.AGENTPRESS_WEBHOOK_SECRET!,
});

await client.webhooks.send({
  action: "review_response",
  payload: {
    eventType: "review.created",
    externalId: "review-123",
    data: { rating: 5, text: "Great service." },
  },
});

For manual scripts, use the same action webhook endpoint and match the configured verification scheme.

HMAC SHA-256 cURL

export AGENTPRESS_API_URL="https://api.agent.press"
export AGENTPRESS_ORG="your-org-slug"
export AGENTPRESS_WEBHOOK="review_response"
export AGENTPRESS_WEBHOOK_SECRET="whsec_your_secret"

body='{"eventType":"review.created","externalId":"review-123","data":{"rating":5,"text":"Great service."}}'
timestamp="$(date +%s)"
signature="$(
  printf '%s.%s' "$timestamp" "$body" |
    openssl dgst -sha256 -hmac "$AGENTPRESS_WEBHOOK_SECRET" -hex |
    awk '{print $2}'
)"

curl -sS -X POST "$AGENTPRESS_API_URL/webhooks/actions/$AGENTPRESS_ORG/$AGENTPRESS_WEBHOOK" \
  -H "Content-Type: application/json" \
  -H "x-webhook-timestamp: $timestamp" \
  -H "x-webhook-signature: v1=$signature" \
  --data "$body"

Shared Token cURL

curl -sS -X POST "$AGENTPRESS_API_URL/webhooks/actions/$AGENTPRESS_ORG/$AGENTPRESS_WEBHOOK" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AGENTPRESS_WEBHOOK_SECRET" \
  --data '{"eventType":"review.created","externalId":"review-123","data":{"rating":5}}'

No-Header Capability URL cURL

Only use this for webhooks intentionally configured with verificationScheme: "none":

curl -sS -X POST "$AGENTPRESS_CAPABILITY_URL" \
  -H "Content-Type: application/json" \
  --data '{"eventType":"review.created","externalId":"review-123","data":{"rating":5}}'

Responses

CodeMeaning
200Action created, or an existing action was returned for the same externalId.
202Delivery accepted and buffered because configuration or resolution is incomplete.
400Invalid JSON or payload shape.
401Missing, stale, or invalid verification headers.
403Webhook disabled, origin blocked, or sender IP blocked.
404Organization or webhook identifier not found.
413Payload exceeds the configured maximum size.
429Per-IP or per-webhook rate limit exceeded.
500Internal persistence error.

Action Created

{
  "success": true,
  "actionId": "uuid-of-created-action"
}

Duplicate External ID

{
  "success": true,
  "actionId": "uuid-of-existing-action",
  "alreadyExists": true
}

Buffered Delivery

{
  "success": true,
  "buffered": true,
  "eventId": "uuid-of-webhook-event",
  "reason": "user_unresolved"
}

Common buffered reasons include no_action_rule_configured, action_rule_not_found, action_rule_disabled, user_unresolved, and action_create_failed.

Debugging in the Actions Ledger

After sending a webhook, open Actions in the console.

  • A successful webhook-created action appears as a normal action row with ingress metadata attached.
  • A buffered, skipped, failed, or dead-lettered delivery appears as a delivery-only row until it creates an action.
  • Open a row to inspect the summary, ingress payload, redacted headers, verification scheme, lifecycle events, approval state, conversation, result, and outbound callbacks.
  • Replay is available only for replayable delivery failures, skipped deliveries, or dead-lettered rows.

Outbound Callbacks

AgentPress can send signed HTTP callbacks as actions move through their lifecycle. A callback can subscribe to any mix of these public event types:

Event TypeMeaning
action.pending_approvalA tool call is staged and needs approval.
action.approvedThe action was approved or is currently executing. Not terminal.
action.completedThe action finished successfully.
action.failedThe action failed during processing or execution.
action.rejectedThe staged action was rejected.
action.expiredThe staged action expired before approval.

Outbound callbacks use Svix-style signatures. Verify them with client.webhooks.constructEvent(), verify(), or verifyOrThrow() from the SDK. For setup details and payload examples, see Receiving Callbacks.

Operational Notes

  • New action webhooks default to verificationScheme: "svix".
  • client.webhooks.send() posts to /webhooks/actions/{org}/{identifier}.
  • Existing SDK listener aliases are compatibility APIs; new integrations should use client.webhooks.send().
  • Manual scripts copied from the older listener URL should update the URL and verification headers to match the configured action webhook.
  • Per-webhook rate limiting defaults to 100 requests per minute and is capped at 1000 requests per minute.
  • Secrets are encrypted at rest and only shown on create or rotate.

On this page