orakel
Docs navigation

Canonical

Webhooks

Generic HTTPS webhook destination for pushing Orakel records to any custom system.

Updated 2026-04-21

Overview

The webhook destination type POSTs Orakel records to a user-supplied URL. Use it when there is no dedicated adapter for your target system, or when you want the raw record shape.

Each push is one HTTPS request. The body contains every company fetched for the request, with financials, roles, sub-units, and licenses attached.

Setup

  1. Create a destination of type webhook:
    curl -X POST https://orakel.cloud/api/destinations \
      -H "Authorization: Bearer $ORAKEL_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "my-webhook",
        "type": "webhook",
        "config": {
          "url": "https://ingest.example.com/orakel",
          "method": "POST",
          "headers": { "X-Tenant": "acme" }
        }
      }'
  2. Trigger a push:
    curl -X POST https://orakel.cloud/api/push/my-webhook \
      -H "Authorization: Bearer $ORAKEL_KEY" \
      -H "Content-Type: application/json" \
      -d '{"orgNumbers": ["923609016","912345678"]}'

Payload shape

One POST per push call. The body wraps every company for the request in a single companies array:

{
  "companies": [
    {
      "id": "...",
      "orgNumber": "923609016",
      "country": "NO",
      "name": "Equinor ASA",
      "orgFormCode": "ASA",
      "naceCode1": "06.100",
      "naceDescription1": "Utvinning av raolje",
      "employeeCount": 21000,
      "businessAddressStreet": "...",
      "primaryDomain": "equinor.com",
      "enrichedDomains": ["equinor.com"],
      "technologies": [{ "name": "...", "category": "...", "confidence": 0.9 }],
      "financials": [{ "periodTo": "2024-12-31", "revenue": 1000000000, "netResult": 100000000 }],
      "roles": [{ "roleTypeCode": "DAGL", "personFirstName": "...", "personLastName": "..." }],
      "subUnits": [{ "orgNumber": "...", "name": "..." }],
      "licenses": [{ "licenseTypeCode": "01SKJE", "venueName": "..." }]
    }
  ]
}

The shape matches the Prisma Company model with financials, roles, subUnits, licenses included. See the CompanyWithRelations type in lib/push/adapters/types.ts for the authoritative structure.

Configuration

Field Type Required Description
url string yes HTTPS endpoint.
method string no Defaults to POST.
headers object no Extra headers merged with Content-Type: application/json.
{
  "url": "https://ingest.example.com/orakel",
  "method": "POST",
  "headers": {
    "X-Tenant": "acme",
    "Authorization": "Bearer ..."
  }
}

Encrypted at rest (AES-256-GCM).

Security

  • Always use HTTPS. Orakel does not emit secrets in the body, but API tokens you set in headers travel with every request.
  • Shared-secret auth: put your auth token in a custom header via config.headers. The receiver verifies it.
  • Signature verification: the current webhook adapter does not sign the body. If you need HMAC signing, route through a custom adapter or add a signing proxy in front of your receiver.
  • IP allowlisting: Orakel calls come from the Hetzner Helsinki egress. Contact hello@orakel.cloud for the current outbound IP range.

Push behavior

  • One POST per POST /api/push/:destinationName call, regardless of orgNumbers length.
  • Non-2xx response: the entire batch is marked failed and returned in the push result errors[].
  • Network error or timeout: same — whole batch fails.
  • No retry. No exponential backoff. The caller retries by re-invoking /api/push/:destinationName.
  • Receiver-side rate limiting is the receiver's problem. Batch sizes are bounded by the push endpoint's 100-org limit.

Gotchas

  • All-or-nothing batch: one non-2xx fails the whole push. Split large batches into smaller pushes if your receiver is flaky.
  • No built-in dedupe: the same org number pushed twice produces two requests. Key on orgNumber receiver-side.
  • X-Forwarded-* handling: if your receiver sits behind a proxy and you verify any signature yourself, reconstruct the public URL before hashing. Orakel pushes straight, but internal receivers often don't.
  • TLS required: plain HTTP URLs are accepted at config time but should not be used. No guarantees about what intermediaries will do.
  • Body size: companies carry nested financials, roles, sub-units, and licenses. A 100-org push can exceed several hundred KB.
  • Destinations endpoint — CRUD for destinations and push trigger
  • Attio — dedicated adapter with upsert semantics
  • HubSpot — dedicated adapter with OAuth and webhook-driven enrichment