{
  "openapi": "3.1.0",
  "info": {
    "title": "Heilo API",
    "version": "0.12.0",
    "summary": "Read the calls Heilo captured, and receive them as they happen.",
    "description": "Heilo records, transcribes and summarises phone calls, then feeds them into the CRM you already use.\n\nThis version is **read-only**: three operations, all `GET`. Writing back into Heilo, listing contacts and\nplacing outbound calls are on the roadmap at https://www.heilo.io/docs/api — email support@heilo.io with a\nuse case and it moves up.\n\nMost integrations do not poll this API at all. Subscribe a webhook, receive each call as it finishes, and\nuse these endpoints to backfill or to re-read one call.",
    "contact": {
      "name": "Heilo support",
      "email": "support@heilo.io",
      "url": "https://www.heilo.io/docs/api"
    },
    "termsOfService": "https://www.heilo.io/pl/terms"
  },
  "servers": [
    {
      "url": "https://www.heilo.io/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "Who the calling key is."
    },
    {
      "name": "Calls",
      "description": "The captured calls themselves."
    }
  ],
  "paths": {
    "/me": {
      "get": {
        "operationId": "getMe",
        "tags": [
          "Authentication"
        ],
        "summary": "Identify the calling key",
        "description": "Returns the key's own id, organization, scopes and rate limit. Reaches no other resource, so it needs no scope — this is the call to make when testing a connection.",
        "responses": {
          "200": {
            "description": "The calling key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/ApiKeyIdentity"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The organization/key owner has no active subscription entitlement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the required scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Hourly rate limit for this key exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server or database failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The public API is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/calls": {
      "get": {
        "operationId": "listCalls",
        "tags": [
          "Calls"
        ],
        "summary": "List calls",
        "description": "Newest first, paginated. `has_more` says whether another page exists — prefer it over comparing counts.",
        "security": [
          {
            "bearerAuth": [
              "read.calls"
            ]
          }
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "default": 1,
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 9007199254740991
            },
            "description": "Page number, starting at 1."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "default": 20,
              "type": "integer",
              "exclusiveMinimum": 0,
              "maximum": 100
            },
            "description": "How many calls to return, at most 100."
          },
          {
            "name": "dateFrom",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Earliest `created_at`, inclusive (ISO 8601)."
          },
          {
            "name": "dateTo",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Latest `created_at`, inclusive (ISO 8601)."
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "inbound",
                "outbound"
              ]
            },
            "description": "Keep only inbound or only outbound calls."
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "new",
                "to_call",
                "contacted",
                "qualified"
              ]
            },
            "description": "Keep only calls in this CRM status."
          },
          {
            "name": "phone",
            "in": "query",
            "required": false,
            "schema": {
              "description": "Return only calls with this customer number — the caller on an inbound call, the counterparty on an outbound one, matching customer_phone_e164. Spaces, hyphens, dots and parentheses are ignored; the leading + is optional.",
              "type": "string",
              "minLength": 3,
              "maxLength": 40
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "items",
                        "has_more",
                        "page",
                        "limit"
                      ],
                      "properties": {
                        "items": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Call"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "page": {
                          "type": "integer"
                        },
                        "limit": {
                          "type": "integer"
                        }
                      }
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "A query parameter failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The organization/key owner has no active subscription entitlement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the required scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Hourly rate limit for this key exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server or database failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The public API is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/calls/{id}/recording-url": {
      "get": {
        "operationId": "getCallRecordingUrl",
        "tags": [
          "Calls"
        ],
        "summary": "Mint a download link for a call recording",
        "description": "Returns a freshly signed link to the call audio, valid for 15 minutes. It is a download permission with a deadline rather than a durable reference: store the `call_id` and ask again, because a link saved in a CRM field is dead within the hour. The recording URL delivered inside a `call.recording.ready` webhook is signed for seven days; this endpoint is how you reach the audio after that. A call belonging to another organization answers 404, exactly as one that does not exist.",
        "security": [
          {
            "bearerAuth": [
              "read.recordings"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The `call_id` from a list response or a webhook."
          }
        ],
        "responses": {
          "200": {
            "description": "A signed link and the moment it stops working. Served with `Cache-Control: no-store` — the body carries a credential.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/RecordingUrl"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The organization/key owner has no active subscription entitlement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the required scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such call in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Ask again later. `RECORDING_NOT_READY` — the audio has not arrived yet. `RECORDING_WITHHELD` — the call is in the trash or scheduled for deletion, and both of those can be undone by the customer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Gone for good, for one of two reasons. `RECORDING_DELETED` — the retention sweep removed it and it is not recoverable; stop retrying and stop re-queueing. `RECORDING_NOT_CAPTURED` — nothing was ever there to delete: an outbound call was refused by the capture gate when it started, placed in no-recording mode, or ended in a state that is never recorded. An inbound call whose recording was refused keeps answering 409, because a recording can still follow. One generic message covers every reason. A call merely in the trash answers 409, not this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Hourly rate limit for this key exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server or database failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The public API is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/calls/{id}": {
      "get": {
        "operationId": "getCall",
        "tags": [
          "Calls"
        ],
        "summary": "Read one call",
        "description": "A call belonging to another organization answers 404, never 403 — the id space is deliberately not enumerable.",
        "security": [
          {
            "bearerAuth": [
              "read.calls"
            ]
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The `call_id` from a list response or a webhook."
          }
        ],
        "responses": {
          "200": {
            "description": "The call.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "const": true
                    },
                    "data": {
                      "$ref": "#/components/schemas/Call"
                    },
                    "meta": {
                      "type": "object",
                      "properties": {
                        "timestamp": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid Bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "The organization/key owner has no active subscription entitlement.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The key is valid but lacks the required scope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such call in this organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Hourly rate limit for this key exhausted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Server or database failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "The public API is temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "verification": {
      "post": {
        "operationId": "verifyWebhookSubscription",
        "summary": "Unsigned endpoint-verification handshake",
        "description": "Only verifies the endpoint. Do not process it as business data. Permissive mode accepts any 2xx; strict mode also requires a JSON echo of challenge. A signed webhook.test is sent separately by the manual Test action.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookVerification"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Endpoint accepted. In strict mode, return the exact challenge.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "challenge": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "description": "Verification failed; the subscription remains inactive."
          }
        }
      }
    },
    "call": {
      "post": {
        "operationId": "receiveCallEvent",
        "summary": "An event Heilo sends to your endpoint",
        "description": "Every event shares this envelope. Verify `Heilo-Signature` (HMAC-SHA256 over timestamp + \".\" + raw body) before trusting it, and deduplicate on `event_id` — a delivery can repeat.",
        "security": [],
        "parameters": [
          {
            "name": "heilo-signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "t=<unix_seconds>,v1=<hex_hmac>. HMAC-SHA256 covers <unix_seconds>.<exact_raw_body_bytes>, keyed with the subscription secret. Check timestamp tolerance and compare in constant time BEFORE parsing JSON."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "401": {
            "description": "Immediately pauses the subscription and dead-letters the delivery."
          },
          "403": {
            "description": "Immediately pauses the subscription and dead-letters the delivery."
          },
          "410": {
            "description": "Two consecutive 410 responses pause the subscription."
          },
          "422": {
            "description": "Immediately pauses the subscription and dead-letters the delivery."
          },
          "2XX": {
            "description": "Acknowledges HTTP receipt; does not prove the automation or CRM write succeeded."
          },
          "default": {
            "description": "Retryable failures use 2 min, 5 min, 30 min and 2 h backoff with jitter: five attempts total. Fifty consecutive transient failures pause the subscription. Delivery order is not guaranteed."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An API key from Settings → Integrations, sent as `Authorization: Bearer <key>`."
      }
    },
    "schemas": {
      "Call": {
        "type": "object",
        "properties": {
          "call_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
            "description": "Stable call identifier."
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "caller_phone": {
            "type": "string",
            "description": "Stored caller phone. Use customer_phone_e164 when matching the customer in a CRM."
          },
          "customer_phone_e164": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The customer on this call, in both directions — the same value the call.completed webhook sends under this name. Null when the carrier gave no usable number."
          },
          "customer_phone_national": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The same number in its own country's national format, ready for a CRM phone field; null when the stored value is not a usable number."
          },
          "company_phone_e164": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "The number this call came in on, or went out from — your Heilo number, or your own number when you forward it to Heilo. Null when not recorded."
          },
          "app_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Permanent link to the call in Heilo; unlike a recording link it does not expire. Opening it requires a Heilo login."
          },
          "caller_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "duration": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Recording length in seconds, not total call duration."
          },
          "crm_status": {
            "type": "string",
            "enum": [
              "new",
              "to_call",
              "contacted",
              "qualified"
            ],
            "description": "Legacy CRM kanban status."
          },
          "review_status": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "new",
                  "needs_decision",
                  "needs_review",
                  "handled",
                  "no_action"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "outbound_lifecycle": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "ringing_agent",
                  "agent_answered",
                  "ringing_customer",
                  "in_progress",
                  "completed",
                  "agent_no_answer",
                  "customer_no_answer",
                  "agent_only",
                  "failed_to_initiate",
                  "reconciliation_required"
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "transcript_processed": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "caller_name": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "summary": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "service_needed": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "followup_email": {
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "subject": {
                            "type": "string"
                          },
                          "body": {
                            "type": "string"
                          },
                          "language": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "subject",
                          "body",
                          "language"
                        ]
                      },
                      {
                        "type": "null"
                      }
                    ],
                    "description": "Draft for the customer to review and send; internal source is not exposed."
                  }
                },
                "required": [
                  "caller_name",
                  "summary",
                  "service_needed",
                  "followup_email"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Allowlisted analysis object; null when absent. Neither raw transcript nor audio access."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When the call was recorded. The webhook envelope also has a created_at and it means something else there — the moment the event was sent; the call time is call_created_at inside its data."
          }
        },
        "required": [
          "call_id",
          "direction",
          "caller_phone",
          "customer_phone_e164",
          "customer_phone_national",
          "company_phone_e164",
          "app_url",
          "caller_name",
          "duration",
          "crm_status",
          "review_status",
          "outbound_lifecycle",
          "transcript_processed",
          "created_at"
        ]
      },
      "ApiKeyIdentity": {
        "type": "object",
        "required": [
          "api_key_id",
          "user_id",
          "organization_id",
          "scopes"
        ],
        "properties": {
          "api_key_id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rate_limit_per_hour": {
            "type": "integer"
          },
          "environment": {
            "type": "string"
          }
        }
      },
      "RecordingUrl": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
          }
        },
        "required": [
          "url",
          "expires_at"
        ]
      },
      "Error": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Machine-readable code, e.g. `UNAUTHORIZED`."
              },
              "message": {
                "type": "string"
              }
            }
          }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "api_version": {
            "type": "string",
            "const": "2026-06-15"
          },
          "event_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "event_type": {
            "type": "string",
            "enum": [
              "call.outbound.attempted",
              "call.outbound.lifecycle_repaired",
              "call.completed",
              "call.transcribed",
              "call.recording.ready",
              "call.failed",
              "call.deletion_scheduled",
              "call.recording.deleted",
              "call.followup_email.drafted",
              "call.tracker.matched",
              "contact.created",
              "contact.updated",
              "webhook.test"
            ]
          },
          "resource_id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
          },
          "data": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {}
          }
        },
        "required": [
          "api_version",
          "event_id",
          "event_type",
          "resource_id",
          "created_at",
          "data"
        ],
        "additionalProperties": false,
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.outbound.attempted"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "agent_user_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "customer_phone": {
                    "type": "string"
                  },
                  "customer_phone_e164": {
                    "type": "string"
                  },
                  "outbound_lifecycle": {
                    "type": "string",
                    "enum": [
                      "completed",
                      "agent_only",
                      "agent_no_answer",
                      "customer_no_answer",
                      "failed_to_initiate"
                    ]
                  },
                  "duration": {
                    "anyOf": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "attempted_at": {
                    "type": "string"
                  },
                  "has_recording": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "call_id",
                  "agent_user_id",
                  "customer_phone",
                  "customer_phone_e164",
                  "outbound_lifecycle",
                  "duration",
                  "attempted_at",
                  "has_recording"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\nagent_user_id (string (uuid)): ID of the Heilo user who placed the call.\ncustomer_phone (string): The dialled customer number.\ncustomer_phone_e164 (string): Customer number for matching a CRM contact; null if unavailable in call.completed.\noutbound_lifecycle ('completed' | 'agent_no_answer' | 'customer_no_answer' | 'failed_to_initiate'): Final state that triggered the event; completed means the call connected and ended normally.\nduration (number | null): Call duration in seconds; null when the call failed at initiation or the duration is not known yet.\nattempted_at (string (ISO 8601)): When the event was emitted (ISO 8601).\nhas_recording (boolean): true only when outbound_lifecycle is completed and the call was not in no_recording mode — the recording then follows as call.recording.ready.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.outbound.attempted",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "agent_user_id": "2ce127a0-c621-483d-b576-e68f69d95e84",
                  "customer_phone": "+447700900123",
                  "customer_phone_e164": "+447700900123",
                  "outbound_lifecycle": "completed",
                  "duration": 95,
                  "attempted_at": "2026-09-08T10:00:00Z",
                  "has_recording": true
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.outbound.lifecycle_repaired"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "previous_lifecycle": {
                    "type": "string",
                    "const": "agent_only"
                  },
                  "new_lifecycle": {
                    "type": "string",
                    "const": "completed"
                  },
                  "repaired_at": {
                    "type": "string"
                  }
                },
                "required": [
                  "call_id",
                  "previous_lifecycle",
                  "new_lifecycle",
                  "repaired_at"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\nprevious_lifecycle ('agent_only'): State before the correction; currently always agent_only.\nnew_lifecycle ('completed'): State after the correction; currently always completed.\nrepaired_at (string (ISO 8601)): When the correction happened (ISO 8601).",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.outbound.lifecycle_repaired",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "previous_lifecycle": "agent_only",
                  "new_lifecycle": "completed",
                  "repaired_at": "2026-09-08T10:00:00Z"
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.completed"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "inbound",
                      "outbound"
                    ]
                  },
                  "caller_phone": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "customer_phone_e164": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "customer_phone_national": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "company_phone_e164": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "call_created_at": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "app_url": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "duration": {
                    "type": "number"
                  },
                  "transcript_processed": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  },
                  "transcript_original": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "recording_url": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "call_id",
                  "direction",
                  "caller_phone",
                  "customer_phone_e164",
                  "duration",
                  "transcript_processed",
                  "transcript_original",
                  "recording_url"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\ndirection ('inbound' | 'outbound'): Call direction.\ncaller_phone (string | null): Stored caller number; null if unavailable in call.completed.\ncustomer_phone_e164 (string | null): Customer number for matching a CRM contact; null if unavailable in call.completed.\ncustomer_phone_national (string | null): The same customer number in national format, ready to paste into a CRM phone field; null when the stored value is not a usable number, as with a withheld caller ID.\ncompany_phone_e164 (string | null): The number this call came in on, or went out from. Usually your Heilo number; on a forwarded call it is your own number that forwarded it. Use it to route notes when you run more than one number; null when not recorded.\ncall_created_at (string (ISO 8601) | null): When the call was recorded in Heilo (ISO 8601). Not the same as the envelope created_at, which is the moment the event was sent and can be days later for a repaired event.\napp_url (string | null): Permanent link to the call in Heilo. Unlike recording_url it does not expire, so it is safe to store in a CRM record. Opening it requires a Heilo login.\nduration (number): Recording length in seconds.\nrecording_url (string | null): Stable link to the recording; null when the recording was not yet stored at processing time.\ntranscript_processed (object): Processed call analysis — see the transcript_processed field reference in this section.\ntranscript_original (string | null): Raw verbatim transcript; null when unavailable.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.completed",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "direction": "inbound",
                  "caller_phone": "+447700900123",
                  "customer_phone_e164": "+447700900123",
                  "customer_phone_national": "07700 900123",
                  "company_phone_e164": "+48222630000",
                  "call_created_at": "2026-09-08T10:00:00Z",
                  "app_url": "https://www.heilo.io/calls/3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "duration": 87,
                  "recording_url": "https://www.heilo.io/api/v1/calls/3fa85f64-5717-4562-b3fc-2c963f66afa6/recording.mp3?token=EXAMPLE&exp=1789466400",
                  "transcript_processed": {
                    "caller_name": "Alicja Testowa",
                    "counterparty_name": null,
                    "caller_location": "Warszawa, Mokotów",
                    "client_address": "ul. Przykładowa 10",
                    "client_city": "Warszawa",
                    "client_country": "Poland",
                    "service_needed": "Tynki gipsowe w mieszkaniu 65m²",
                    "services_match": true,
                    "preferred_date": "w przyszłym tygodniu",
                    "additional_details": "Klient wspomniał o terminie do końca czerwca. Druga rozmowa po wycenie.",
                    "lead_score": 7,
                    "summary": "Prośba o wycenę tynków gipsowych w mieszkaniu 65 m² na Mokotowie.",
                    "subject": "Wycena tynków — Warszawa, 65 m²",
                    "detected_language": "pl"
                  },
                  "transcript_original": "Proszę o ofertę w przyszłym tygodniu."
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.transcribed"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "transcript_original": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "transcript_processed": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  }
                },
                "required": [
                  "call_id",
                  "transcript_original",
                  "transcript_processed"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\ntranscript_original (string | null): Raw verbatim transcript; null when unavailable.\ntranscript_processed (object): Processed call analysis — see the transcript_processed field reference in this section.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.transcribed",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "transcript_original": "Proszę o ofertę w przyszłym tygodniu.",
                  "transcript_processed": {
                    "caller_name": "Alicja Testowa",
                    "counterparty_name": null,
                    "caller_location": "Warszawa, Mokotów",
                    "client_address": "ul. Przykładowa 10",
                    "client_city": "Warszawa",
                    "client_country": "Poland",
                    "service_needed": "Tynki gipsowe w mieszkaniu 65m²",
                    "services_match": true,
                    "preferred_date": "w przyszłym tygodniu",
                    "additional_details": "Klient wspomniał o terminie do końca czerwca. Druga rozmowa po wycenie.",
                    "lead_score": 7,
                    "summary": "Prośba o wycenę tynków gipsowych w mieszkaniu 65 m² na Mokotowie.",
                    "subject": "Wycena tynków — Warszawa, 65 m²",
                    "detected_language": "pl"
                  }
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.recording.ready"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "recording_url": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "duration": {
                    "anyOf": [
                      {
                        "type": "number"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "call_id",
                  "recording_url",
                  "duration"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\nrecording_url (string | null): Stable link to the recording; in rare cases null when the link could not be generated.\nduration (number | null): Duration in seconds; null when not reported yet.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.recording.ready",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "recording_url": "https://www.heilo.io/api/v1/calls/3fa85f64-5717-4562-b3fc-2c963f66afa6/recording.mp3?token=EXAMPLE&exp=1789466400",
                  "duration": 87
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.failed"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "outbound_lifecycle": {
                    "type": "string",
                    "enum": [
                      "agent_only",
                      "agent_no_answer",
                      "customer_no_answer",
                      "failed_to_initiate"
                    ]
                  },
                  "failure_reason": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "call_id",
                  "outbound_lifecycle",
                  "failure_reason"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\noutbound_lifecycle ('agent_no_answer' | 'customer_no_answer' | 'failed_to_initiate'): Which stage of the outbound call failed.\nfailure_reason (string | null): Machine-readable failure code (e.g. customer_busy, agent_no_confirmation); can be null.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.failed",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "outbound_lifecycle": "customer_no_answer",
                  "failure_reason": "customer_busy"
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.deletion_scheduled"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "pending_deletion_at": {
                    "type": "string"
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "consent_not_asked",
                      "consent_withdrawn",
                      "retention_expired",
                      "user_erasure"
                    ]
                  }
                },
                "required": [
                  "call_id",
                  "pending_deletion_at",
                  "reason"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\npending_deletion_at (string (ISO 8601)): When the data will be permanently deleted (ISO 8601).\nreason ('consent_not_asked' | 'consent_withdrawn' | 'retention_expired' | 'user_erasure'): Why the deletion was scheduled.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.deletion_scheduled",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "pending_deletion_at": "2026-09-09T10:00:00Z",
                  "reason": "user_erasure"
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.recording.deleted"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "reason": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "recording_sid": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "deletion_kind": {
                    "type": "string",
                    "enum": [
                      "hard_deleted",
                      "twilio_404"
                    ]
                  },
                  "deleted_at": {
                    "type": "string"
                  }
                },
                "required": [
                  "call_id",
                  "reason",
                  "recording_sid",
                  "deletion_kind",
                  "deleted_at"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\nreason (string | null): Reason recorded when the deletion was scheduled; can be null.\nrecording_sid (string | null): Twilio recording ID; null when it could not be resolved.\ndeletion_kind ('hard_deleted' | 'twilio_404'): hard_deleted = deleted by Heilo; twilio_404 = the file was already gone on Twilio's side.\ndeleted_at (string (ISO 8601)): When the recording was deleted (ISO 8601).",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.recording.deleted",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "reason": "user_erasure",
                  "recording_sid": null,
                  "deletion_kind": "twilio_404",
                  "deleted_at": "2026-09-08T10:00:00Z"
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.followup_email.drafted"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "subject": {
                    "type": "string"
                  },
                  "body": {
                    "type": "string"
                  },
                  "language": {
                    "type": "string"
                  }
                },
                "required": [
                  "call_id",
                  "subject",
                  "body",
                  "language"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\nsubject (string): Email subject, ready to paste.\nbody (string): Email body as plain text, WITHOUT a signature — the rep adds their own when sending.\nlanguage (string): Draft language: the detected call language, falling back to the account language configured in Heilo (not a per-number setting).",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.followup_email.drafted",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "subject": "Oferta po rozmowie",
                  "body": "Dzień dobry, przesyłam ustalenia naszej rozmowy.",
                  "language": "pl"
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "call.tracker.matched"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "matches": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "tracker_id": {
                          "type": "string",
                          "format": "uuid",
                          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                        },
                        "name": {
                          "type": "string"
                        },
                        "matched_terms": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "excerpts": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      },
                      "required": [
                        "tracker_id",
                        "name",
                        "matched_terms",
                        "excerpts"
                      ],
                      "additionalProperties": false
                    }
                  }
                },
                "required": [
                  "call_id",
                  "matches"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\nmatches (object[]): The trackers matched in this call.\nmatches[].tracker_id (string (uuid)): Identifier of the configured tracker.\nmatches[].name (string): Tracker name.\nmatches[].matched_terms (string[]): Matched terms.\nmatches[].excerpts (string[]): Transcript excerpts; may be empty.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "call.tracker.matched",
                "resource_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "matches": [
                    {
                      "tracker_id": "cd794362-6117-44d1-b487-c5e7e1ea9082",
                      "name": "Oferta",
                      "matched_terms": [
                        "ofertę"
                      ],
                      "excerpts": [
                        "Proszę o ofertę w przyszłym tygodniu."
                      ]
                    }
                  ]
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "contact.created"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "contact": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "phone": {
                        "type": "string"
                      },
                      "first_name": {
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "last_name": {
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "email": {
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "company": {
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "id",
                      "phone",
                      "first_name",
                      "last_name",
                      "email",
                      "company"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "contact"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "contact (object): Full snapshot of the new contact (fields below).\ncontact.id (string (uuid)): Contact ID in Heilo.\ncontact.phone (string): The contact's phone number.\ncontact.first_name (string | null): null when not provided.\ncontact.last_name (string | null): null when not provided.\ncontact.email (string | null): null when not provided.\ncontact.company (string | null): null when not provided.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "contact.created",
                "resource_id": "86c68698-d54c-43e7-bbbc-328499b98d12",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "contact": {
                    "id": "86c68698-d54c-43e7-bbbc-328499b98d12",
                    "phone": "+447700900123",
                    "first_name": "Alicja",
                    "last_name": "Testowa",
                    "email": "alicja@example.com",
                    "company": null
                  }
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "contact.updated"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "contact_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "diff": {
                    "type": "object",
                    "properties": {
                      "first_name": {
                        "type": "string"
                      },
                      "last_name": {
                        "type": "string"
                      },
                      "email": {
                        "type": "string"
                      },
                      "phone": {
                        "type": "string"
                      },
                      "company": {
                        "type": "string"
                      },
                      "notes": {
                        "type": "string"
                      },
                      "tags": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "required": [
                  "contact_id",
                  "diff"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "contact_id (string (uuid)): ID of the updated contact.\ndiff (object (partial)): Only the fields that changed — keys absent from diff were not modified.",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "contact.updated",
                "resource_id": "86c68698-d54c-43e7-bbbc-328499b98d12",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "contact_id": "86c68698-d54c-43e7-bbbc-328499b98d12",
                  "diff": {
                    "company": "Example",
                    "notes": "Oddzwonić",
                    "tags": [
                      "oferta"
                    ]
                  }
                }
              }
            ]
          },
          {
            "type": "object",
            "properties": {
              "api_version": {
                "type": "string",
                "const": "2026-06-15"
              },
              "event_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "event_type": {
                "type": "string",
                "const": "webhook.test"
              },
              "resource_id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "created_at": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
              },
              "data": {
                "type": "object",
                "properties": {
                  "call_id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "inbound",
                      "outbound"
                    ]
                  },
                  "caller_phone": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "customer_phone_e164": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "customer_phone_national": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "company_phone_e164": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "call_created_at": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "app_url": {
                    "description": "Present on events emitted from 2026-09-18; absent on a replay of a call recorded before that.",
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "duration": {
                    "type": "number"
                  },
                  "transcript_processed": {
                    "type": "object",
                    "propertyNames": {
                      "type": "string"
                    },
                    "additionalProperties": {}
                  },
                  "transcript_original": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "recording_url": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "_test": {
                    "type": "boolean",
                    "const": true
                  },
                  "_message": {
                    "type": "string"
                  },
                  "_sent_at": {
                    "type": "string"
                  }
                },
                "required": [
                  "call_id",
                  "direction",
                  "caller_phone",
                  "customer_phone_e164",
                  "duration",
                  "transcript_processed",
                  "transcript_original",
                  "recording_url",
                  "_test",
                  "_message",
                  "_sent_at"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "api_version",
              "event_id",
              "event_type",
              "resource_id",
              "created_at",
              "data"
            ],
            "additionalProperties": false,
            "description": "call_id (string (uuid)): Call ID in Heilo. For business call events it equals resource_id; in webhook.test it is a synthetic call ID.\ndirection ('inbound' | 'outbound'): Call direction.\ncaller_phone (string | null): Stored caller number; null if unavailable in call.completed.\ncustomer_phone_e164 (string | null): Customer number for matching a CRM contact; null if unavailable in call.completed.\ncustomer_phone_national (string | null): The same customer number in national format, ready to paste into a CRM phone field; null when the stored value is not a usable number, as with a withheld caller ID.\ncompany_phone_e164 (string | null): The number this call came in on, or went out from. Usually your Heilo number; on a forwarded call it is your own number that forwarded it. Use it to route notes when you run more than one number; null when not recorded.\ncall_created_at (string (ISO 8601) | null): When the call was recorded in Heilo (ISO 8601). Not the same as the envelope created_at, which is the moment the event was sent and can be days later for a repaired event.\napp_url (string | null): Permanent link to the call in Heilo. Unlike recording_url it does not expire, so it is safe to store in a CRM record. Opening it requires a Heilo login.\nduration (number): Recording length in seconds.\nrecording_url (string | null): Stable link to the recording; null when the recording was not yet stored at processing time.\ntranscript_processed (object): Processed call analysis — see the transcript_processed field reference in this section.\ntranscript_original (string | null): Raw verbatim transcript; null when unavailable.\n_test (true): Always true — tells the test message apart from real events.\n_message (string): Human-readable note that this is a test.\n_sent_at (string (ISO 8601)): When the test was sent (ISO 8601).",
            "examples": [
              {
                "api_version": "2026-06-15",
                "event_id": "1bf3a5e2-4f82-4335-988e-4e044fa648d4",
                "event_type": "webhook.test",
                "resource_id": "c2d38cc8-bc1d-4336-91ea-51b8a549a882",
                "created_at": "2026-09-08T10:00:00Z",
                "data": {
                  "call_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "direction": "inbound",
                  "caller_phone": "+447700900123",
                  "customer_phone_e164": "+447700900123",
                  "customer_phone_national": "07700 900123",
                  "company_phone_e164": "+48222630000",
                  "call_created_at": "2026-09-08T10:00:00Z",
                  "app_url": "https://www.heilo.io/calls/3fa85f64-5717-4562-b3fc-2c963f66afa6",
                  "duration": 87,
                  "recording_url": "https://www.heilo.io/api/v1/calls/3fa85f64-5717-4562-b3fc-2c963f66afa6/recording.mp3?token=EXAMPLE&exp=1789466400",
                  "transcript_processed": {
                    "caller_name": "Alicja Testowa",
                    "counterparty_name": null,
                    "caller_location": "Warszawa, Mokotów",
                    "client_address": "ul. Przykładowa 10",
                    "client_city": "Warszawa",
                    "client_country": "Poland",
                    "service_needed": "Tynki gipsowe w mieszkaniu 65m²",
                    "services_match": true,
                    "preferred_date": "w przyszłym tygodniu",
                    "additional_details": "Klient wspomniał o terminie do końca czerwca. Druga rozmowa po wycenie.",
                    "lead_score": 7,
                    "summary": "Prośba o wycenę tynków gipsowych w mieszkaniu 65 m² na Mokotowie.",
                    "subject": "Wycena tynków — Warszawa, 65 m²",
                    "detected_language": "pl"
                  },
                  "transcript_original": "Proszę o ofertę w przyszłym tygodniu.",
                  "_test": true,
                  "_message": "Synthetic webhook test; do not create CRM records.",
                  "_sent_at": "2026-09-08T10:00:00Z"
                }
              }
            ]
          }
        ]
      },
      "WebhookVerification": {
        "type": "object",
        "properties": {
          "event_type": {
            "type": "string",
            "const": "webhook.subscription.verify"
          },
          "challenge": {
            "type": "string"
          }
        },
        "required": [
          "event_type",
          "challenge"
        ],
        "additionalProperties": false
      }
    }
  }
}
