Skip to main content
Preview feature. Webhooks are currently in preview. Seamless delivery is not guaranteed, and the API, payload shape, and behaviour are likely to change in future releases.
Quarterzip can notify your systems in real time when an event happens in your workspace. You register an HTTPS endpoint and Quarterzip sends a signed POST request to it whenever a relevant event occurs. Today there is a single event — call.completed — sent once a call has finished and its transcript and ratings are available.

Set up an endpoint

  1. In the Quarterzip dashboard, go to Settings → Webhooks.
  2. Enter the HTTPS URL that should receive events.
  3. Toggle Enabled on and save.
When you save a URL for the first time, Quarterzip generates a signing secret and displays it once. Copy it immediately and store it somewhere secure, such as a secrets manager — never in source control. You’ll need it to verify that incoming requests genuinely came from Quarterzip, and it will not be shown again.
The endpoint URL must use https and must resolve to a public IP address. Localhost, private-network, and link-local addresses are rejected.
You can configure one endpoint per workspace.

Rotating the secret

If a secret is ever leaked, click Rotate secret on the same settings page. A new secret is generated and shown once.
Rotation takes effect immediately. The old secret stops verifying as soon as you rotate, so update your endpoint with the new value promptly to avoid rejecting deliveries in between.

The call.completed event

Quarterzip sends this event after a call finishes processing. The request is a JSON POST with Content-Type: application/json.

Example payload

{
  "type": "call.completed",
  "timestamp": "2026-06-19T14:32:05.123456+00:00",
  "data": {
    "call_id": "abc123",
    "workspace_id": "ws_456",
    "deployment_id": "dep_789",
    "started_at": "2026-06-19T14:28:51.000000+00:00",
    "ended_at": "2026-06-19T14:31:48.000000+00:00",
    "is_test": false,
    "is_coaching": false,
    "is_phone": true,
    "is_sdk": false,
    "call_quality_rating": 5,
    "product_experience_rating": 4,
    "transcript": [
      { "speaker": "agent", "text": "Hi, thanks for calling — how can I help?" },
      { "speaker": "user", "text": "I'd like to update my billing address." }
    ]
  }
}

Fields

FieldTypeNotes
typestringAlways call.completed for this event.
timestampstring (ISO 8601)When the event was sent. Also available as a Unix timestamp in the webhook-timestamp header.
data.call_idstringUnique ID of the call.
data.workspace_idstringThe workspace the call belongs to.
data.deployment_idstringThe agent deployment the call used.
data.started_atstring (ISO 8601)When the call started.
data.ended_atstring (ISO 8601) | nullWhen the call ended. null if not recorded.
data.is_testbooleantrue for test calls.
data.is_coachingbooleantrue for coaching calls.
data.is_phonebooleantrue if the call came in over the phone.
data.is_sdkbooleantrue if the call was placed through the embedded SDK.
data.call_quality_ratinginteger | nullEnd-user call-quality rating, if collected.
data.product_experience_ratinginteger | nullEnd-user product-experience rating, if collected.
data.transcriptarrayOrdered turns, each { "speaker": string, "text": string }. May be empty — see truncation below.

Which calls trigger a webhook

  • Test and coaching calls are delivered. Use the is_test and is_coaching flags to filter them out if you only care about real traffic.

Payload size and transcript truncation

Payloads are capped at 1 MB. If a call’s transcript would push the payload over that limit, Quarterzip drops the transcript and adds a flag so you can detect it:
{
  "type": "call.completed",
  "data": {
    "call_id": "abc123",
    "transcript": [],
    "transcript_truncated": true
  }
}

Verify the signature

Always verify the signature before trusting a webhook. It proves the request came from Quarterzip and wasn’t tampered with in transit.
Quarterzip signs every delivery using the open Standard Webhooks specification, so you can use the official standardwebhooks libraries to verify with a few lines of code. Each request includes three headers:
HeaderDescription
webhook-idUnique ID for this delivery (for example, call_completed_abc123).
webhook-timestampSend time as a Unix timestamp (seconds).
webhook-signatureSpace-separated list of v1,<base64-signature> values.
Verify against the raw request body, exactly as received. Do not parse and re-serialize the JSON first, or the signature won’t match.
# pip install standardwebhooks
from standardwebhooks import Webhook

# The whole "whsec_..." string shown when you created or rotated the secret.
WEBHOOK_SECRET = "whsec_..."

def handle_webhook(raw_body: bytes, headers: dict[str, str]) -> None:
    wh = Webhook(WEBHOOK_SECRET)

    # Raises on an invalid signature or an out-of-tolerance timestamp.
    payload = wh.verify(raw_body, headers)

    if payload["type"] == "call.completed":
        process_completed_call(payload["data"])
The library also enforces a timestamp tolerance, which protects you against replayed deliveries.

Troubleshooting

SymptomLikely cause
Not receiving any eventsEndpoint is not Enabled, or no real calls are completing.
Signature verification failsVerifying against a re-serialized body instead of the raw bytes, or using an old secret after a rotation.
Can’t save the URLURL must be https and resolve to a public IP — private and localhost addresses are rejected.
Lost the signing secretThe secret is only shown once — rotate it from Settings → Webhooks to generate a new one.