id: webhook name: Outbound Webhook version: 1.0.0 description: "Outbound webhook — POST from Riposte to any third-party solution: a JSON document, a normalised incident event, a form body, or a raw payload. Per-instance destination, static and per-call headers, bearer/basic/custom-header auth, HMAC-SHA256 body signature, retry on 429/5xx. Stdlib-only, no extra Python dependencies." changelog: "1.0.0 — Initial release: post_json, send_event, post_form, post_raw, and a non-intrusive test_connection." category: notification # --------------------------------------------------------------------------- # The destination lives on the instance, not in the command inputs: one instance # per receiving solution, named after it. A per-call `url` is refused unless the # operator opens allow_url_override — the agent can then be handed this # integration without also being handed an arbitrary HTTP client. # # `url` is sensitive because a webhook URL is a credential in practice: Slack, # Teams, Discord, Zapier and n8n all carry the token in the path. Command output # echoes scheme://host only, never the path or the query. # --------------------------------------------------------------------------- config_schema: properties: url: type: string description: "Destination URL, e.g. https://hooks.example.com/services/T000/B000/xxxx. Stored encrypted; commands echo back the host only." x-soar-sensitive: true method: type: string description: "Default HTTP verb — POST (default), PUT or PATCH." default: POST auth_type: type: string description: "none (default) | bearer | basic | header." default: none auth_token: type: string description: "Bearer token, basic password, or the custom header value — depending on auth_type." x-soar-sensitive: true auth_username: type: string description: "Username, for auth_type=basic." auth_header_name: type: string description: "Header carrying auth_token, for auth_type=header." default: Authorization hmac_secret: type: string description: "Shared secret. When set, every request carries an HMAC-SHA256 signature of its body so the receiver can prove it came from Riposte." x-soar-sensitive: true hmac_header: type: string description: "Header carrying the signature." default: X-Riposte-Signature hmac_prefix: type: string description: "Prefix before the hex digest. Set to an empty string for a bare digest." default: "sha256=" hmac_scheme: type: string description: "body (default) signs the raw body. timestamp_body signs '.' and sends the timestamp header too — replay-resistant, the scheme Slack and Stripe use." default: body hmac_timestamp_header: type: string description: "Timestamp header, for hmac_scheme=timestamp_body." default: X-Riposte-Timestamp default_headers: type: object description: "Static headers added to every request, e.g. {\"X-Tenant\": \"acme\"}. Per-call headers win over these." user_agent: type: string description: "User-Agent sent to the receiver." default: "Riposte-SOAR/1.0 (+webhook)" content_type: type: string description: "Content-Type for JSON commands." default: application/json timeout_seconds: type: number description: "Per-attempt timeout, 1-40s. The sandbox kills a command at 60s, so total budget stays under it." default: 20 max_attempts: type: number description: "Attempts, 1-5. Retries only on 408/425/429/5xx and on connection failures, honouring Retry-After." default: 3 verify_tls: type: boolean description: "Verify the receiver's TLS certificate. Turn off only for an internal host with a private CA." default: true allow_url_override: type: boolean description: "Let a command supply its own url, ignoring the one above. Off by default — an on instance is an arbitrary HTTP client for anything that can call it." default: false allow_private_targets: type: boolean description: "Allow destinations resolving to private, loopback or link-local addresses. Off by default (SSRF guard); turn on to post to a receiver on the internal network." default: false allow_plaintext_http: type: boolean description: "Allow http:// destinations. Off by default — a plaintext webhook ships its payload and its auth header in clear." default: false test_sends_ping: type: boolean description: "Make the Test button POST a small ping payload. Off by default: the background health checker runs this command on a schedule, and every run would reach the third party." default: false required: - url commands: - id: post_json name: webhook-post-json description: "POST a JSON document to the configured destination. The payload is sent as-is — use this when the receiver defines the schema." risk: safe_write category: notify inputs_schema: properties: payload: type: object description: "Body to send. An object or an array; a JSON string is parsed." url: type: string description: "Destination override (refused unless allow_url_override is on)." path: type: string description: "Appended to the instance URL's path, e.g. /events." headers: type: object description: "Extra headers for this call only." method: type: string description: "POST | PUT | PATCH — overrides the instance default." required: - payload outputs_schema: properties: ok: { type: boolean } status: { type: number } attempts: { type: number } target: { type: string } duration_ms: { type: number } response: { type: object } - id: send_event name: webhook-send-event description: "POST a normalised Riposte event envelope — incident id, title, severity, status, link — to the destination. Use this to notify a solution from a playbook without composing JSON by hand." risk: safe_write category: notify inputs_schema: properties: event: type: string description: "Event name, e.g. incident.created, incident.closed. Default: incident.notified." incident_id: type: string description: "Riposte incident id." title: type: string description: "Incident title / message subject." severity: type: string description: "Severity, as a number (1-5) or a label — passed through untouched." status: type: string description: "Incident status." description: type: string description: "Free-text body." incident_url: type: string description: "Deep link back into Riposte." tags: type: string description: "Comma-separated list, or an array." data: type: object description: "Anything extra, sent under `data`." url: type: string description: "Destination override (refused unless allow_url_override is on)." path: type: string description: "Appended to the instance URL's path." headers: type: object description: "Extra headers for this call only." required: - title outputs_schema: properties: ok: { type: boolean } status: { type: number } attempts: { type: number } target: { type: string } duration_ms: { type: number } event: { type: string } - id: post_form name: webhook-post-form description: "POST an application/x-www-form-urlencoded body. For receivers that predate JSON webhooks." risk: safe_write category: notify inputs_schema: properties: fields: type: object description: "Field name to value. A list value is repeated as multiple parameters." url: type: string description: "Destination override (refused unless allow_url_override is on)." path: type: string description: "Appended to the instance URL's path." headers: type: object description: "Extra headers for this call only." method: type: string description: "POST | PUT | PATCH." required: - fields outputs_schema: properties: ok: { type: boolean } status: { type: number } attempts: { type: number } target: { type: string } duration_ms: { type: number } - id: post_raw name: webhook-post-raw description: "POST a body verbatim with the Content-Type you choose — XML, CSV, NDJSON, plain text. The escape hatch for a receiver none of the other commands fits." risk: safe_write category: notify inputs_schema: properties: body: type: string description: "Body, sent verbatim as UTF-8." content_type: type: string description: "Content-Type header. Default: text/plain; charset=utf-8." url: type: string description: "Destination override (refused unless allow_url_override is on)." path: type: string description: "Appended to the instance URL's path." headers: type: object description: "Extra headers for this call only." method: type: string description: "POST | PUT | PATCH." required: - body outputs_schema: properties: ok: { type: boolean } status: { type: number } attempts: { type: number } target: { type: string } duration_ms: { type: number } - id: test_connection name: webhook-test-connection description: "Check the destination without disturbing it: validate the URL and auth settings, then open a TCP/TLS connection to the host. Set test_sends_ping on the instance to POST a real ping instead." risk: read inputs_schema: properties: {} required: [] outputs_schema: properties: ok: { type: boolean } target: { type: string } mode: { type: string } auth: { type: string } signed: { type: boolean }