Webhook Guide

Webhook Guide

Webhooks let Plutto push events to your server as they happen, instead of you
polling the API. You configure one or more endpoints for your organization,
subscribe each to the events you care about, and Plutto delivers a signed
POST request every time a matching event fires.

How it works

  1. You create a webhook endpoint (a URL) in your Plutto dashboard and choose
    which events it should receive.
  2. When something happens (a validation finishes, an information request is
    completed, etc.), Plutto fans the event out to every enabled endpoint
    subscribed to that event type
    .
  3. Each delivery is recorded as an event with its own id and full attempt
    history, retried automatically on failure, and redeliverable on demand.

Endpoints are configured once per organization and reused across all
validations — you do not pass a webhook URL on each request.


Setup

1. Create an endpoint

In the Plutto dashboard go to Settings → Webhooks → Add webhook and set:

FieldDescription
NameA label to identify the endpoint (e.g. Production).
URLThe HTTPS URL that will receive the events.
Subscribed eventsThe event types this endpoint receives (see below).
Authentication typeNone, Secret key, or OAuth (see Authentication).
StatusEnabled / disabled. A disabled endpoint receives nothing.

You can create multiple endpoints (e.g. one per environment or per team), each
with its own URL, subscriptions, and authentication.

2. Authentication

Choose one authentication type per endpoint:

  • None — no auth header is sent. Only recommended for testing.

  • Secret key — generate a signing key from the endpoint screen. Plutto sends
    it on every request as:

    Authorization: Bearer whsec_xxxxxxxxxxxxxxxx

    Verify this header on your side to confirm the request came from Plutto. The
    key is shown only once at generation time and is not recoverable
    store it securely.

  • OAuth (client credentials) — Plutto fetches a bearer token from your OAuth
    provider (grant_type=client_credentials) and sends it as
    Authorization: Bearer <token>, refreshing it automatically. OAuth must be
    configured at the organization level first — contact support to set it up. An
    endpoint using OAuth cannot be enabled until OAuth is configured.

All endpoint URLs must be HTTP(S); HTTPS is strongly recommended in production.

3. Inspect endpoints via API (optional)

Your endpoints can be listed programmatically:

GET /api/v2/webhook_endpoints
{
  "webhook_endpoints": [
    {
      "id": "whe_5a6141dec2bc384e",
      "name": "Production",
      "url": "https://your-server.com/webhooks",
      "enabled": true,
      "subscribed_events": [
        "data_source_run_batch.ready",
        "information_request.all_items_collected"
      ],
      "auth_type": "secret",
      "created_at": "2026-07-01T12:00:00.000Z",
      "updated_at": "2026-07-01T12:00:00.000Z"
    }
  ]
}

Creating, editing, and generating secrets is done from the dashboard, not the
public API.


The delivery envelope

Every webhook is a POST with Content-Type: application/json. Plutto wraps the
serialized resource in a small envelope:

{
  "type": "data_source_run_batch.ready",
  "data_source_run_batch": { ... }
}
FieldDescription
typeThe event type that fired.
resource keyThe serialized resource. The key depends on the event type (see next table).

Resource key per event

The resource is nested under a key derived from the event type:

Event typeResource key
entity_validation.stakeholder.createdstakeholder
entity_validation.formation.readyformation
data_source_run_batch.readydata_source_run_batch
data_source_run_batch.consolidated_report_readydata_source_run_batch
data_source_run_batch.child_batches_readydata_source_run_batch
data_source_run_batch.data_source_run.readydata_source_run
information_request.all_items_collectedinformation_request
information_request.analysis_successinformation_request
information_request.status_updatedinformation_request
information_request.evaluatedinformation_request
information_request.requestable_item.extractedrequestable_item
information_request.assignment.updatedassignment

Event Types

Subscribe an endpoint to any combination of the following.

Entity Validation Events

EventTrigger
entity_validation.stakeholder.createdStakeholders added to an entity validation
entity_validation.formation.readyThe entity's societary (formation) information and legal report PDF are ready

Data Source Run Batch Events

EventTrigger
data_source_run_batch.readyAll runs of the main entity completed successfully
data_source_run_batch.data_source_run.readyAn individual run completed
data_source_run_batch.consolidated_report_readyPDF report generated
data_source_run_batch.child_batches_readyThe main entity's batch and every related entity (partner) batch finished

Information Request Events

EventTrigger
information_request.all_items_collectedAll items submitted by recipient
information_request.requestable_item.extractedA single requestable item finished extraction
information_request.assignment.updatedReviewer assignment status changed
information_request.status_updatedStatus changed
information_request.analysis_successAI analysis completed
information_request.evaluatedResponsible completed the after-evaluation flow (fires only when submitted)

Payloads

All payloads below are shown with the full delivery envelope.

Entity Validation — stakeholder.created

{
  "type": "entity_validation.stakeholder.created",
  "stakeholder": {
    "id": "evl_e63adcdf5c702350",
    "tin": "76613324-k",
    "display_name": "Sociedad Comercial Bon Cas Limitada",
    "name": "Sociedad Comercial Bon Cas Limitada",
    "surname": "",
    "category": "business",
    "country": "CL",
    "status": "created",
    "created_at": "2026-02-02T15:51:12.105Z",
    "information_requests": [
      {
        "id": "irq_bc8cf77370892cf1",
        "name": "Nuevo cliente",
        "created_at": "2026-02-02T15:51:12.188Z",
        "recipient_url": "https://..."
      }
    ],
    "data_source_run_batches": [
      {
        "id": "dsrb_75eae47d0060cf5d",
        "runs_succeeded": true,
        "entity_validation_id": "evl_e63adcdf5c702350",
        "information_request_id": "irq_bc8cf77370892cf1",
        "execution_type": "direct",
        "request_type": "normal",
        "created_at": "2026-02-02T15:51:12.115Z",
        "updated_at": "2026-02-02T15:54:34.166Z",
        "consolidated_report_url": "https://..."
      }
    ],
    "related_entities": [
      {
        "id": "evl_557f33434aebd60e",
        "tin": "20025804-5",
        "country": "CL",
        "display_name": "Alejandro Ignacio Castillo Valera",
        "name": "Alejandro Ignacio",
        "surname": "Castillo Valera",
        "category": "person",
        "risk_level": null
      }
    ],
    "latest_raised_alerts": [],
    "latest_data_source_runs": [
      {
        "id": "dsr_c83d588a5c0bcc2f",
        "type": "cl/bankruptcy_bulletins_search",
        "entity_validation_id": "evl_e63adcdf5c702350",
        "data_source_run_batch_id": "dsrb_75eae47d0060cf5d",
        "created_at": "2026-02-02T15:51:12.127Z",
        "succeeded_at": "2026-02-02T15:51:12.506Z",
        "status": "succeeded",
        "data": {
          "id": "bbs_6bc2ce7c1f115d56",
          "bankruptcy_bulletins": {
            "count": 0,
            "records_url": "/api/v2/data_source_runs/dsr_c83d588a5c0bcc2f/records"
          }
        }
      }
    ]
  }
}

The stakeholder object contains the parent entity validation with its full
data, including the newly created related_entities (the stakeholders that
were just added).

Entity Validation — formation.ready

Fired when the societary (formation) information of a business entity is complete and its
legal report PDF has been generated. Today this applies to entities incorporated in Chile's
Registro Electrónico de Empresas y Sociedades (RES/EUD), whose constitution is downloaded and
processed automatically. The event fires exactly once per validation (and once more on each
revalidation).

{
  "type": "entity_validation.formation.ready",
  "formation": {
    "entity_validation_id": "evl_af478f4c2970c61c",
    "data_source_run_batch_id": "dsrb_c7c342cfd44b8f2a",
    "tin": "78486463-4",
    "name": "PURO CORTE SPA",
    "fantasy_name": null,
    "country": "CL",
    "source": "constitution",
    "company_type": "spa",
    "constitution_date": "2026-08-09T00:00:00Z",
    "registry": "Registro Electrónico de Empresas y Sociedades",
    "duration": "indefinida",
    "activity_description": "la compra, venta, distribucion...",
    "requires_manual_review": false,
    "manual_review_codes": [],
    "administration": {
      "administration_type": "por un Gerente General",
      "simple_signature": true
    },
    "equity": {
      "total_amount": 500000,
      "total_shares": 100,
      "currency": "CLP",
      "shares_type": "acciones nominativas, de una misma serie, ordinarias y sin valor nominal."
    },
    "stakeholders": [
      {
        "entity_validation_id": "evl_2819dd7a2a49c5c4",
        "display_name": "Johander Juvenal Bitriaga Guzman",
        "tin": "26423822-6",
        "category": "person",
        "roles": ["shareholder", "manager", "tax_representative"],
        "ubo": true,
        "participation": 1.0,
        "shares": 100,
        "amount": 500000,
        "faculties": [
          { "key": "contratar cuentas corrientes", "code": "F001-GFB", "present": true },
          { "key": "garantizar obligaciones ajenas", "code": "F031-GGT", "present": false }
        ]
      }
    ],
    "documents": [
      {
        "id": "doc_2473f2946d76ea7e",
        "name": "Escritura de constitución",
        "label": "constitution",
        "source": "RES",
        "cve": "CR91crZ1tZRY",
        "url": "https://...",
        "created_at": "2026-08-19T15:43:45.685Z"
      }
    ],
    "legal_report_url": "https://..."
  }
}

stakeholders is the unified societary relation list: roles come from the constitution
(shareholder, manager, general_manager, tax_representative, director),
participation is a 0–1 fraction, and faculties are the banking/legal powers detected for
managers. documents are the source files (the constitution deed with its registry cve),
and legal_report_url points to the generated PDF. Document and report URLs are pre-signed
and expire after 24 hours — download them on receipt or re-fetch through the API.

Data Source Run Batch — ready

{
  "type": "data_source_run_batch.ready",
  "data_source_run_batch": {
    "id": "dsrb_5a6141dec2bc384e",
    "runs_succeeded": true,
    "entity_validation_id": "evl_xxx",
    "tin": "77092317-4",
    "information_request_id": "irq_xxx",
    "parent_batch_id": null,
    "execution_type": "direct",
    "request_type": "normal",
    "created_at": "2026-07-01T19:48:18.056Z",
    "updated_at": "2026-07-01T19:50:22.000Z",
    "consolidated_report_url": "https://...",
    "data_source_runs": [
      {
        "id": "dsr_xxx",
        "type": "cl/tax_situation_search",
        "status": "succeeded",
        "created_at": "2026-07-01T19:48:18.056Z",
        "succeeded_at": "2026-07-01T19:49:00.000Z"
      }
    ],
    "raised_alerts": [],
    "child_batches": []
  }
}

Every batch object is self-similar: it carries its own tin, its own
raised_alerts, and its child_batches — one batch object per related entity
(partner), with the same shape. Alerts always belong to the batch that contains
them.

Child Batches Ready — Related Entity (Partner) Alerts

When Plutto validates a company, it also discovers and validates its related
entities (partners / UBOs). Those validations run asynchronously: they may
still be running when ready fires for the main entity.

data_source_run_batch.child_batches_ready fires once the main entity's batch
and all of its partner batches have completed. It is delivered exactly
once
per validation, and it also fires when the entity has no partners — so you
can always treat it as the "everything, partners included, is done" signal.

child_batches_ready is gated by an organization-level setting. If you need it
and aren't receiving it, contact support to enable it for your organization.
Independently of that setting, the consolidated report is generated only once
the whole tree is ready, so consolidated_report_ready reflects every partner
and fires after the tree completes (and after child_batches_ready when enabled).

{
  "type": "data_source_run_batch.child_batches_ready",
  "data_source_run_batch": {
    "id": "dsrb_5a6141dec2bc384e",
    "runs_succeeded": true,
    "entity_validation_id": "evl_main",
    "tin": "77092317-4",
    "parent_batch_id": null,
    "execution_type": "direct",
    "raised_alerts": [
      {
        "id": "rlal_xxx",
        "name": "Causas legales en el último año",
        "severity": "medium",
        "entity_validation_id": "evl_main",
        "data_source_run_batch_id": "dsrb_5a6141dec2bc384e"
      }
    ],
    "child_batches": [
      {
        "id": "dsrb_child1",
        "runs_succeeded": true,
        "entity_validation_id": "evl_partner1",
        "tin": "12345678-9",
        "parent_batch_id": "dsrb_5a6141dec2bc384e",
        "execution_type": "related",
        "raised_alerts": [
          {
            "id": "rlal_yyy",
            "name": "Hallazgos listas Warning",
            "severity": "high",
            "entity_validation_id": "evl_partner1",
            "data_source_run_batch_id": "dsrb_child1"
          }
        ]
      }
    ]
  }
}

To compile every alert of the validation, partners included:

const batch = payload.data_source_run_batch;
const allAlerts = batch.raised_alerts.concat(
  batch.child_batches.flatMap((child) => child.raised_alerts)
);

Good to know:

  • Timing. child_batches_ready fires once the last partner finishes (usually within
    minutes of the main entity's ready). consolidated_report_ready then fires after
    it, because the report is generated from the completed tree and includes every partner.
    On the earlier ready event, child_batches is already present but partners may show
    runs_succeeded: false with partial alerts — a partner's alerts are final once its own
    runs_succeeded is true.
  • Partner identity. Each child batch carries the partner's tin. For full identity
    (name, category, risk level), call GET /api/v2/entity_validations/:id on the main
    entity — related_entities[] lists every partner.
  • Failure behavior. If a partner's data source fails definitively, neither this event
    nor the automatic consolidated report is emitted for that validation (the report can
    still be generated on demand). The rest of the core flow is never blocked by a partner.

Individual Data Source Run — data_source_run.ready

{
  "type": "data_source_run_batch.data_source_run.ready",
  "data_source_run": {
    "id": "dsr_5ac79b8b3ecfaacf",
    "type": "cl/tax_situation_search",
    "entity_validation_id": "evl_xxx",
    "data_source_run_batch_id": "dsrb_xxx",
    "created_at": "2026-07-01T19:48:18.056Z",
    "succeeded_at": "2026-07-01T19:49:00.000Z",
    "status": "succeeded"
  }
}

Possible status values:

  • succeeded - Run completed successfully
  • created - Run in progress
  • temporary_error - Temporary failure (will retry)
  • definitive_error - Permanent failure
  • missing_input_data - Waiting for required input

Information Request — all_items_collected

{
  "type": "information_request.all_items_collected",
  "information_request": {
    "id": "irq_5a6141dec2bc384e",
    "name": "KYC Request",
    "status": "pending",
    "entity_validation_id": "evl_xxx",
    "template_id": "irt_xxx",
    "recipient_url": "https://...",
    "consolidated_report_url": null,
    "responsible_comment": null,
    "requestable_items": ["..."],
    "data_source_run_batch": {},
    "assignments": ["..."]
  }
}

Full payload includes requestable_items, data_source_run_batch,
assignments, information_sheet, and analysis_chat. See the API reference
for the complete schema.

responsible_comment is included in every Information Request payload. It
remains null until the responsible reviewer submits the after-evaluation form
(typically delivered alongside information_request.evaluated), and is exposed
at the payload root so consumers don't need to iterate assignments[].

Key resource fields

FieldDescription
data_source_run_batch.idUnique batch identifier
runs_succeededBoolean indicating overall success
entity_validation_idParent validation reference
information_request_idAssociated request (if any)
consolidated_report_urlPDF download URL when available
data_source_runsArray of individual results
tinTax identification number of the batch's entity
raised_alertsRule engine alerts of the batch's own entity
child_batchesRelated entity (partner) batches, each with its own tin, state and raised_alerts
related_entitiesStakeholders linked to entity validation
responsible_commentComment from responsible reviewer (Information Request, null until after-evaluation is submitted)

Delivery, retries, and redelivery

  • Retries. A delivery that doesn't get a 2xx response is retried up to
    20 times with exponential backoff. A disabled endpoint fails immediately
    without retrying.
  • Event status. Each delivery is tracked as an event with a status:
    pending, succeeded, failed, or expired. The full attempt history
    (response code, body, duration, timestamp) is visible per event.
  • Redelivery. From Settings → Webhooks → (endpoint) → Event history you
    can inspect any delivery and redeliver it manually — useful to replay an
    event after fixing an outage on your side.

Best Practices

  1. Expect duplicates — retries and manual redeliveries can send the same
    event again; make your handler idempotent.
  2. Verify the Authorization header — reject requests that don't carry your
    endpoint's secret (or a valid OAuth token).
  3. Respond with HTTP 200 quickly — acknowledge receipt, then process
    asynchronously; don't block the response on your own work.
  4. Store data_source_run_batch.id to correlate related events.
  5. Check runs_succeeded before processing results.
  6. Subscribe to child_batches_ready if your compliance decision depends on
    the people behind the company — it is the only event that guarantees every
    partner's alerts are final.

Event Flow

1. Entity Validation / Information Request created
   |
   +-- Data source runs start
   |   |
   |   +-- Individual runs complete
   |   |   |
   |   |   +-- data_source_run_batch.data_source_run.ready (per run)
   |   |
   |   +-- ALL runs of the MAIN entity complete
   |   |   |
   |   |   +-- data_source_run_batch.ready
   |   |
   |   +-- ALL runs of the main entity AND every partner complete
   |       |
   |       +-- data_source_run_batch.child_batches_ready
   |       |
   |       +-- Report generation starts (includes every partner)
   |           |
   |           +-- data_source_run_batch.consolidated_report_ready
   |
   +-- Stakeholders created (via societary data or API)
       |
       +-- entity_validation.stakeholder.created

Appendix — Legacy (V1) webhooks

Deprecated. The legacy model is being phased out as organizations migrate
to the endpoint-based model above. New integrations should use endpoints.

In the legacy model there are no organization-level endpoints. Instead you pass a
webhook_url on each resource you create, and Plutto delivers events to that URL:

POST /api/v2/entity_validations
{
  "tin": "77092317-4",
  "country": "CL",
  "webhook_url": "https://your-server.com/webhooks",
  "information_request": {
    "template_id": "irt_xxx",
    "webhook_url": "https://your-server.com/irq-webhook"
  }
}

Differences from the endpoint model:

  • No subscriptions. The URL receives all events for that resource; you cannot
    filter by event type.
  • Identifier. The configured URL is echoed back inside the resource as a
    nested webhook object (id = wbh_..., webhook_url, created_at); it is
    null when no webhook_url was set.
  • Authentication. A single organization-wide Bearer token (configured via
    support) is sent on all webhooks, rather than a per-endpoint secret.
{
  "type": "data_source_run_batch.ready",
  "data_source_run_batch": {
    "id": "dsrb_5a6141dec2bc384e",
    "runs_succeeded": true,
    "webhook": {
      "id": "wbh_xxx",
      "webhook_url": "https://your-server.com/webhooks",
      "created_at": "2024-01-15T19:48:18.056Z"
    }
  }
}