Scoped service API
Create an integration in Organizations → API integrations. Select its client spaces, permissions and a maximum credit usage per cycle. Generate a key after a recent login, then save the displayed secret in your secret manager. The secret is shown once and cannot be recovered from the administration interface.
Service identities survive the departure of their creator. Revoking an identity or key stops its access. Rotation supports a bounded overlap; all keys of an identity share its credit ceiling. Increasing an identity's access does not silently expand old keys: issue a new key explicitly.
Authentication and scope
Authorization: Bearer pb2b_<key-id>_<secret>
Search, investigation and other client operations have an explicit organization and client scope:
/api/v2/service/organizations/{organization_id}/clients/{client_id}
Use the API base URL shown by your installation. Cookie sessions, personal API keys and personal OAuth tokens cannot substitute for a service key. Do not put secrets in query strings, logs or workflow JSON. Use separate identities for integrations with different client-access requirements.
| Scope | Implemented operations |
|---|---|
search:read | Searches, batch submission, job states/results, bounded cancellation and retry |
tools:execute | Bounded asset tools |
exports:create | Private export jobs, files and cancellation |
cases:read | Cases, notes, revision history and evidence in assigned client spaces |
cases:write, evidence:write, reports:render, watches:write | Explicit machine writes described below |
reports:read | Report lists, versions and permitted downloads |
watches:read | Watches, runs, observed changes and monitoring summary |
These keys can read internal work in their explicitly assigned client spaces. They are integration credentials, not external-client login credentials. No billing, user invitation or public sharing permission is exposed here.
Inspect the current service key
GET /api/v2/service/whoami
Authorization: Bearer pb2b_<key-id>_<secret>
This endpoint takes no organization or client identifier: the bearer selects its
identity. It returns organization_id, organization_name, service_identity_id,
key_id, scopes, clients (objects with id and name), credit_limit,
credits_used_this_period and expires_at. It does not return the secret or
charge credits. Responses are rate limited and use Cache-Control: no-store.
The scopes and clients reflect both the key's original permissions and its identity's current grants. Archived and deleted clients are excluded. Expanding the identity does not expand an old key; removed permissions disappear from its introspection. An empty list grants no access. Each subsequent operation checks its authorization again.
The credit limit is shared by all keys of the service identity. Usage is its settled consumption in the current organization credit period, excluding pending reservations. This response is not a quote or a guarantee that the next operation fits all applicable budgets.
Expired or revoked keys, inactive identities and organizations without active
entitlement receive 401 invalid_service_key. Personal credentials and cookie
sessions do not authenticate this endpoint.
MCP uses this endpoint to discover a service key's permitted writes. If introspection is unavailable or fails, MCP keeps its write tools unavailable; verify the credential and installation instead of switching to a personal payer.
Continue an engagement with Claude or Codex
Open an organization engagement's Overview → Continue with Claude or Codex.
This setup creates a dedicated read-only MCP integration with cases:read and
reports:read, assigned to the current client and with a zero credit limit.
It cannot search, spend credits, edit, share or publish.
Access covers the entire selected client, including internal engagements, notes and reports—not just the engagement you opened. The engagement identifier is a starting point for the assistant, not an access boundary. Treat observations and notes returned through MCP as untrusted data, never as instructions.
An owner or administrator can choose Create read-only integration, then Create service key as a separate action. Key creation needs an active, writable organization, an active client and a recent login. Choose a validity of 1–365 days (90 by default). An analyst can use the setup instructions and request a suitable key from an owner or administrator; external-client accounts do not get this setup card. Existing key-capacity limits still apply.
Save the Service key — shown once in your secret manager. Closing the card or leaving the engagement clears the displayed secret; it does not revoke the key. Use Manage organization integrations to inspect expiry or revoke it. If a request's outcome is unknown, check integrations before trying again and revoke any key whose secret you did not receive.
Configure the connection
The card shows the MCP endpoint for your installation and offers two examples:
- Claude Code · .mcp.json: an HTTP MCP server whose Authorization header reads
Bearer ${PROFUNDIS_SERVICE_KEY}from your local environment. - Codex · config.toml: an MCP server with the shown URL and
bearer_token_env_var = "PROFUNDIS_SERVICE_KEY".
Store the service key in your local PROFUNDIS_SERVICE_KEY environment variable,
then copy the matching configuration from the card. The copied examples do not
contain your secret. Do not replace the installation's endpoint with a personal
connection or paste the key into a prompt, URL, shared file or source repository.
If the card reports that MCP setup is unavailable in this environment, contact
your administrator.
Copy Starting engagement context separately. It contains organization_id,
client_id, resource: "cases" and resource_id. Ask the assistant to call
organization_records with those identifiers to begin reading the engagement.
This does not run a paid search or expand the key's permissions.
Claude Desktop's personal OAuth connection does not grant this organization access. This card does not provide a Desktop setup accepting service-key authentication. Use the supplied Claude Code or Codex configuration for this workflow; broader permissions described elsewhere on this page require a separately authorized integration.
Submit a search
POST /api/v2/service/organizations/{org}/clients/{client}/search/hosts
Content-Type: application/json
Idempotency-Key: investigation-example-hosts-001
{"raw_query":"host:example.com","page":1,"results_per_page":30}
Engines: hosts, dns, vhosts (certificates), whois, and unified.
Responses describe an asynchronous job. Poll GET /jobs/{job_id} until it
succeeds, then read GET /jobs/{job_id}/result. A result read does not launch
another search. A failed or cancelled job exposes a diagnostic code.
During the organization trial, WHOIS results never contain natural-person
contacts: registrant_name, registrant_email, and the technical and
administrative contact names and emails. registrant_organization, the
registrar and the name servers stay available. A search, batch, watch, export
or tool that filters, sorts or selects one of those contact fields is refused
with trial_restricted_field (403) before any credit is reserved. Teams and
Business return every field.
Batch with a fixed maximum
POST /api/v2/service/organizations/{org}/clients/{client}/batches
Content-Type: application/json
Idempotency-Key: investigation-example-batch-001
{
"max_credits": 5,
"items": [
{"id":"web","engine":"hosts","query":{"raw_query":"host:example.com","results_per_page":30}},
{"id":"dns","engine":"dns","query":{"raw_query":"host:example.com","results_per_page":30}}
]
}
One batch accepts 1–25 items. The server validates and quotes each search, then
reserves the whole batch atomically. Admission fails without partial enqueue if
its quote exceeds the maximum or any applicable budget. The response includes
batch, items, and can_manage; each item retains its job identifier and state.
After a timeout, retry the same body with the same idempotency key. A different key means a deliberately new operation and can spend credits again.
GET /batchesandGET /batches/{id}: progress and item states.POST /batches/{id}/cancel: cancel unfinished items; preserve completed results.POST /batches/{id}/retry: send{"items":["failed-item-id"],"max_credits":5}with a new stableIdempotency-Keyfor this retry. Only failed/cancelled items may be selected. A new linked batch is created; successes are not rerun.
Use can_manage before offering retry/cancel controls. Another actor's visible
batch is not necessarily manageable by the current integration.
Read investigation data
| Path | Purpose |
|---|---|
/cases, /cases/{id} | Case list/detail |
/cases/{id}/comments, /cases/{id}/evidence | Case notes and signed evidence |
/reports, /reports/{id}/versions | Report definitions and immutable versions |
/report-versions/{id}/download/{format} | html, pdf, json, or csv artifact |
/watches, /watches/{id}/runs | Watch configuration and collection history |
/changes, /monitoring-summary | Observed changes and counters |
All paths are relative to the same organization/client prefix. Evidence and report access is checked again when data is read; possession of a job/report ID alone grants nothing.
Comments on saved graph items
To read a node or relationship thread, use
GET /cases/{case}/comments?graph_kind=node&graph_id={id} (or graph_kind=edge)
under the organization/client prefix. Do not combine these filters with
evidence_id or thread. The cases:read scope and current access to the item
and its supporting evidence are checked again.
A permitted comment write can target that same saved item with
graph_ref: {"kind":"node","id":"saved-node-id"} instead of evidence_id.
It retains the existing cases:write requirement, replay-key rules and
cases:share requirement for client-visible content. Machines cannot read or
create personal notes. The engagement-only thread=case filter excludes these
graph comments.
Validation, hypothesis, exclusion and restoration decisions are reserved for named staff accounts in the browser. A service key cannot supply a human validator by editing a case document. See Assess a saved graph item for the human review workflow and conflict recovery.
Common failures
| Code | Response |
|---|---|
permission_denied / not_found | Check key, assigned client and scopes. Do not switch payer. |
credit_budget_exceeded | Adjust the authorized organization/client/integration ceiling before resubmitting. |
entitlement_required | The workspace is read-only; the owner can review its subscription. |
idempotency_conflict | The same key was used with a different request. Inspect the original operation. |
capacity_exceeded | Reduce the batch/concurrency or wait for existing work. |
trial_restricted_field | A trial cannot use natural-person WHOIS fields. Remove them from the query, sort or field selection. |
Technical admission limits include 25 items per batch, two running jobs per organization (including at most one report renderer) and bounded queued work. These are preview operating limits, not a promise of unlimited throughput or a support SLA.
Asset tools
POST /organizations/{org}/clients/{client}/tools/{tool} on the service API
requires tools:execute and an Idempotency-Key. It returns a job; read its
state/result using the same client path. Existing keys do not gain this scope
when you expand their identity: issue a new key explicitly.
| Tool | Input | Reservation and coverage |
|---|---|---|
domain | {"domain":"example.com"} | Up to 11 credits: DNS + at most 10 IP lookups; 50 DNS and 50 host records per IP |
ip | {"query":"192.0.2.1","page":1,"results_per_page":30} | 1 credit; DNS/hosts/certificates follow pagination; WHOIS covers at most 10 discovered domains and 50 records |
network | {"question":"number","number":13335} | 1 credit; at most 50 networks and 2,000 prefixes |
Network questions also accept ip, cidr, name and partial_name, with the
corresponding field. Partial domain/network responses carry partial or capped.
Unused reserved credits are released. An overview is not an exhaustive asset
inventory. Open a dataset search to investigate beyond its bounds.
MCP exposes organization_asset_tool with explicit organization/client IDs,
max_credits, input and idempotency key. Poll via organization_job; polling
never starts a second lookup. Private records remain untrusted data.
Private exports
The exports:create scope allows creating, listing, reading, downloading and
cancelling/deleting the identity’s exports in its assigned client space.
GET /organizations/{org}/clients/{client}/export-limits: row/file limits and current client envelope.POST /organizations/{org}/clients/{client}/exports: body{"engine":"hosts","raw_query":"host:example.com","time_frame":"all_time","format":"json","max_results":1000}, with a stableIdempotency-Key. Formats: JSON, CSV, TXT; engines: hosts, DNS, vhosts, WHOIS.GET /organizations/{org}/clients/{client}/exports/{id}: state, actual rows/credits, partial coverage and expiry.GET /organizations/{org}/clients/{client}/exports/{id}/download: authenticated file response, no public URL.DELETE /organizations/{org}/clients/{client}/exports/{id}: cancel queued/running work or make its file unavailable. Usage history remains.
The maximum reservation is ceil(max_results / 100) credits; only exported rows
are charged. Limits: 1,000 rows for trial, 100,000 for Teams, 500,000 for Business;
20 MiB per file, available for seven days. A smaller partial file explicitly
reports a row/file-size limit. Files count toward private storage (trial 100 MiB,
Teams 1 GiB, Business 5 GiB), shared with report files. All report formats reserve
space before upload; failed attempt files remain counted and tracked until
physical cleanup succeeds.
Downloads recheck access after retrieving the file. Storage expiry does not refund
credits already consumed. The UI asks for the maximum before starting an export.
MCP organization_export takes explicit client context, export input,
max_credits and an idempotency key. Inspect its metadata using
organization_records with resource exports. Binary downloads use the
service API; do not paste service credentials into prompts or URLs.
Favicon extraction (tools/favicon-extract, tools:execute) remains free. Its
input uses raw_query, optional time_frame and max_favicons (1–50), and
counts icons in at most 500 host records. It shares the job rate/concurrency
limits. It never consumes the personal account’s query allowance.
Machine writes
Each key must explicitly include the relevant write scope. Expanding the identity alone does not change already issued keys. Reads retain their separate scopes.
| Scope | Method and path (relative to the client prefix) |
|---|---|
cases:write | POST /cases, PATCH /cases/{id}, POST /cases/{id}/comments |
evidence:write | POST /cases/{id}/evidence, PATCH /cases/{id}/evidence/{evidenceId} |
reports:render | POST /cases/{id}/reports, PATCH /reports/{id}, POST /reports/{id}/render |
watches:write | POST /watches/preview, POST /watches, PATCH /watches/{id}, POST /watches/{id}/run |
Creates of cases, notes, report definitions and watches require a stable
Idempotency-Key (16–120 characters). Reuse it after a timeout: the same
identity/client/path/body returns the original result, including after key
rotation. A changed body or path with that key returns a conflict. Authorizations
are checked again before replay; revoked keys never recover cached private data.
Concurrent creation is atomic with its audit. Admission permits at most 2,000
new replay records per organization per day; existing replays do not consume this
limit. Never reuse an operation key for a deliberately new creation.
Edits and report renders require the exact current If-Match: "<version>".
A conflict requires reading and reconciling the current resource, not guessing
its next version. Rendering the same revision returns its existing version/job;
failed rendering can be retried explicitly. Renders consume no search credits.
Evidence capture accepts either job_id + record_index or an imported
JSON object, plus visibility and optional annotation. Imported data always
remains user_supplied; it is never relabelled as a Profundis observation.
Capture deduplicates the same source/content. A machine may capture only its own
job results, with the source job's scope still present on the current key.
created_by is null for machine-created resources; service_identity_id and
service_key_id give the actual actor. Machine attribution is included in the
signed evidence manifest.
Watch input includes name, dataset, query, state (active/paused),
scheduled, interval_hours (24/168), max_pages (1–10), and credit_limit per
billing cycle. Preview quotes the bounded collection without running a search.
Runs require an Idempotency-Key; organization, client, integration and watch
budgets apply together. Scheduled runs remain bound to the key that last saved
the watch: expiry, revocation, scope removal or client removal stops execution.
To transfer automation during rotation, PATCH with the new key and current
version. Saving via the UI transfers responsibility to the authenticated person.
Pausing stops queued execution too. Existing observations and credit history stay.
No machine route publishes reports, creates guest links, sends messages, manages
members or modifies billing. Prepare a draft through automation and review it in
the UI. organization_write in MCP exposes case/note/evidence/report preparation
only, with explicit client, action, version and replay key. Its adapter cannot
reach arbitrary URLs or execute paid watch runs. MCP lists it only for a key that
holds cases:write, evidence:write or reports:render; otherwise the MCP
connection stays read-only and you call the endpoints above directly.
Example case creation:
POST /api/v2/service/organizations/{org}/clients/{client}/cases
Authorization: Bearer <service-key>
Idempotency-Key: customer-recon-case-2026-001
Content-Type: application/json
{"title":"External reconnaissance","visibility":"internal"}
Example note (use a different operation key):
{"body":"Review the observed service before publishing.","visibility":"internal"}
Observed subdomain inventory
POST /tools/subdomains with tools:execute and a stable Idempotency-Key:
{"domain":"example.com","max_results":1000}
max_results defaults to 1,000 and accepts 1–10,000. The browser uses 1,000 and
asks for confirmation before reserving up to 6 credits. Price: 1 credit per 100
names for the first 300, then 1 per 300, rounded up; minimum 1 for a completed
inventory. 10,000 names reserve 36 credits. Only the delivered unique names are
charged; unused reservation is released. An unsuccessful source yields no
inventory and no charge. This uses the organization's/client's envelopes, never
the personal subdomain stream, quota or credit estimate.
The result includes results: [{subdomain}], completed, partial, capped,
reason, max_results, source_records_scanned, coverage: observed_dns_names
and the billing context. Names are normalized, deduplicated and sorted. The
collection stops after the requested name limit or 100,000 DNS observations;
partial identifies a limited inventory. completed describes job completion,
not exhaustive domain coverage. Stored evidence retains this coverage metadata.
The collection reads observed DNS; it does not actively scan a customer domain.
MCP organization_asset_tool accepts subdomains with the same input and an
explicit max_credits sufficient for its requested maximum.
Favicon similarity in a client space
tools:execute also covers POST …/tools/favicon-search with { "hash": "1234567", "max_credits": 3 }. Read the current base price from GET …/favicons/pricing before authorizing a request. max_credits is an optional server-enforced ceiling; MCP always supplies it. The result identifies its source_job_id. Hidden bands contain no hash or host count.
GET …/favicons/{source_job_id}/bands/{band}/quote returns the next band's cost and count. To reveal it, submit POST …/tools/favicon-widen with source_job_id, integer band and the authorized max_credits, plus a stable Idempotency-Key. The private source expires after seven days. A band already paid for on this source is free to retrieve again; concurrent successful jobs settle the charge once. The result's billing_context.credits_charged is authoritative.
Machine credentials may only widen their own identity's sources within the selected client and current tools:execute scope. Price changes or exhausted budgets never fall back to personal credits. A new matching search creates a separate source and may incur a new charge.
In the browser, viewed-icon markers and similarity assessments are private to the named user and client space, retained for 90 days, and capped at 5,000 entries per user/client. These assessments do not train the shared matcher. They are not machine API endpoints. Capturing a revealed match as evidence is free; locked placeholders cannot become evidence.
Usage and budget thresholds
Owners and administrators open the organization-wide report in My account → Organization → Credit usage: all clients by default, with totals, a breakdown by member, operation, client, integration or service key, the operation history and a CSV export. In a client space, Credit usage shows that client only. Analysts can open it in their assigned clients, and external users see only their own operations. The credit bar's Credit usage link opens the organization-wide report for owners and administrators; other members get a link to the current client's report while they work in a client. This accounting view does not accept service credentials and does not contain search queries, emails, API secrets or idempotency keys.
Session endpoints under /api/v2/organizations/{orgId}:
GET /usage/report: filter byclient_id,service_identity_id,service_key_id,operation, RFC 3339from/ exclusiveuntil,limit(1–200) andcursor. Defaults to 30 days; maximum interval 366 days. Returns matching totals, grouped client/identity/key/operation usage, and a stable row-order cursor. Each grouped table shows up to 500 entries and signals truncation separately from complete totals.GET /usage/export: the same filters, without a cursor; UTF-8 CSV, up to 10,000 operations. Larger exports are refused: narrow the interval. No research credits are charged.GET /usage/alerts?client_id=…: threshold history for the current accounting cycle, restricted to the caller's visibility. External users only see their shared external envelope's thresholds.
Actual settled consumption triggers 80% and 100% once per cycle and scope (organization, client, external envelope). Reservations alone do not trigger an alert. Adjusting an envelope does not resend thresholds already reached in that cycle. Client/external events can be delivered through a configured budget.threshold_reached webhook; payloads carry identifiers and a state such as client_80, never the accounting breakdown. Organization-wide thresholds appear in the manager's UI. The admin console displays the latest threshold metadata without research content. These are in-app/webhook notifications; they do not enable email delivery by default.
Workspace Pivot
GET …/clients/{client}/pivot/pricing returns search_cost and expand_cost. POST …/clients/{client}/tools/pivot accepts the Pivot request (seed, seed_kind, signals, depth:1, optional expand_node, exclude_nodes, seed_favicon_hash) and optional max_credits. Service scope: tools:execute. At most six signals and 50 excluded nodes; collection deadline 30 seconds; snapshot capped at 499 nodes/1,000 edges. Unindexed seeds cost zero. Failed collection releases credits; usable partial results retain the quoted fixed price and expose partial, signals_completed, signals_failed. No personal billing/entitlement or anonymous fallback is used.
Results carry source_job_id. A named browser user can import 1–10 completed steps through POST …/cases/{case}/pivot, body { "job_ids": ["UUID"], "visibility": "internal" } and If-Match containing the case version. The server reads authorized source jobs, signs inferred-connection evidence and merges the case atomically. External users can import only their own jobs into a shared case with visibility: "client". Oversized sources/merged cases or stale versions reject the entire import. No additional search charge. MCP organization_asset_tool supports Pivot and enforces its authorized ceiling using the workspace price endpoint.
Workspace tracking analysis
POST …/clients/{client}/tools/analytics (tools:execute) accepts { "raw_query": "*.example.com", "level": "2", "only_subs": false, "only_unrelated": false, "max_credits": 1 }. The selector may also be an exact tracking ID; domain-scope filters require a hostname. Depth is a string from "1" to "3". Fixed price: 1 credit. Collection is limited to 30 seconds, 25 source queries and 500 connections; partial, source_queries, failed_queries, coverage, connection_limit and query_limit explain coverage. Source errors with no usable connections release the reservation; valid empty results use the fixed price. Results and signed evidence stay within the client workspace. MCP organization_asset_tool supports analytics with the authorized ceiling.
Organization pipelines and related-name lookups
POST …/clients/{client}/tools/pipeline (tools:execute) accepts the existing steps format, with 1–5 sequential steps, limit 1–50 and optional max_credits. Reservation: one credit per step; only successfully completed sources are charged. Domains selected for enrichment are bounded to 50; expanded queries to 20,000 bytes and 200 filters. Results expose partial, failed_steps and coverage: bounded_live_index_pages. Missing backing-store records, capped aggregations and truncated extraction must not be interpreted as negative evidence.
next_cursor is an authorized completed job ID. Resubmit the same normalized steps and limit with cursor; the server recovers the page and rechecks the job owner, client and service identity. Modified queries and foreign continuations are rejected. Pagination reads the live index; it is not a frozen cross-request snapshot. A continuation or enrichment is a new paid job. Organization and lookalike UI searches, full exports and evidence capture keep this context. MCP organization_asset_tool accepts pipeline and checks the step ceiling before sending.
Workspace portfolio comparison
POST …/clients/{client}/tools/portfolio (tools:execute) accepts { "domains": ["example.com", "example.org"], "max_credits": 2 }. One to ten normalized, unique domains; URLs, IP addresses and query syntax are rejected. The maximum reservation is one credit per domain; a domain with all sources unavailable costs zero. Collection is limited to 45 seconds and three source requests per domain: 500 subdomain host observations, 50 root/WWW observations and one WHOIS record.
The existing comparison schema is preserved, with partial, failed_sources, source_queries, source_limit and analysis_scope. Measurements describe returned observations, not live verification or a complete inventory. Partial analyses withhold automatic findings. Capture a domains_details entry as evidence with this coverage. MCP organization_asset_tool supports portfolio; both synchronous and progress UI entry points use the organization job transport.
Personal investigation copies and conflict recovery
Named browser users may POST …/cases/{case}/import with If-Match: "revision", { "source_name": "Personal investigation", "visibility": "internal", "document": { "summary": "…", "nodes": [], "edges": [] } }. The UI previews a normalized personal investigation JSON export before submission. One to 500 nodes, at most 1,000 edges and a final 256 KiB case document; dangling relations, oversized fields and incoming evidence references are rejected. Node notes are limited to 4,096 characters; positions must be finite and within ±1,000,000. External users require a shared case and visibility: "client".
The copy receives signed, chunked user-supplied evidence; it never becomes a trusted search capture. A stale version or oversized merged case rolls back both evidence and document. Repeating the same import deduplicates entities. Personal originals and their share URLs are unchanged.
POST …/cases/{case}/copy, { "title": "Recovered draft", "document": { … } }, creates a new case from an explicitly submitted draft after checking access to the source. Staff copies start internal; external copies remain client-visible. Draft evidence references are replaced with user-supplied provenance. This supports recovery after an edit conflict without overwriting the winning revision. Neither endpoint charges search credits or exposes a service-key route.
Active key capacity
The server catalogue limits live service keys across the whole organization: trial 1, Teams 3, Business 10. Creating another service identity does not create another allowance. The independent technical limits remain 20 identities, three live keys per identity, and 20 key issuances per hour per organization. Rotation may replace an existing slot with one bounded overlap (up to 24 hours). That overlap counts against other issuances until it expires; repeatedly rotating the same predecessor cannot create extra slots. Revoking a key remains possible when the subscription is read-only or expired.
Verify a job webhook with an integration credential
POST .../webhook-events/verify accepts a stored organization service credential
and a JSON object containing endpoint_id, event_id (the
X-Profundis-Event-ID header), signature (the complete
X-Profundis-Signature header), and body (the exact incoming UTF-8 body as a
string). Body limit: 8 KiB; enclosing JSON limit: 16 KiB. No URL is accepted or
fetched, and verification creates no job or credit operation.
This endpoint currently handles job.completed and endpoint.test. It checks
the active endpoint in the explicit client, the HMAC against its current or
still-valid previous secret, a five-minute timestamp tolerance and the original
stored event. It returns minimal event metadata, never the signing secret or job
payload. A key must have a supported job scope (search:read, tools:execute,
exports:create, reports:render or watches:write); the actual job kind's scope
is checked again. Authentic events belonging to another actor return
ignored: true without resource or state data. Fetch an authorized completed
job through its normal /jobs/{id}/result endpoint.
Verification permits legitimate delivery retries; the consumer remains responsible for event deduplication. The n8n receiver provides an importable example with bounded persistent deduplication and empty webhook responses.