Now inviting stores. Join the waitlist to hear the agent call you.

Join the waitlist

Events

What RowCall posts to your webhook URL as things happen.

A run is waiting

POSTrun.queued → your URL

A run was started and waits for its next attempt (or for calling hours to open).

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.queued

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.queued",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "scheduled",
      "attempts": 0,
      "maxAttempts": 3,
      "nextAttemptAt": "2026-10-13T09:31:02.000Z",
      "lastError": null,
      "outcome": null,
      "answers": {},
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:31:02.000Z",
      "calls": [],
      "actions": []
    }
  }
}

The shopper’s phone is ringing

POSTrun.ringing → your URL

An attempt reached the shopper’s phone.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.ringing

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.ringing",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "dialing",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": null,
      "answers": {},
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:31:02.000Z",
      "calls": [],
      "actions": []
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2"
  }
}

The shopper answered

POSTrun.answered → your URL

The shopper picked up; the agent is talking to them.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.answered

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.answered",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "dialing",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": null,
      "answers": {},
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:31:02.000Z",
      "calls": [],
      "actions": []
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2"
  }
}

An attempt went unanswered

POSTrun.no_answer → your URL

Nobody answered, or the line was busy. The run tries again if it has attempts left.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.no_answer

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.no_answer",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "scheduled",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": "2026-10-13T10:01:30.000Z",
      "lastError": "no_answer",
      "outcome": null,
      "answers": {},
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:31:02.000Z",
      "calls": [],
      "actions": []
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2"
  }
}

A run finished

POSTrun.completed → your URL

The shopper finished the call: run.answers and run.outcome hold what they said.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.completed

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.completed",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "done",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": "confirmed",
      "answers": {
        "placed_order": true,
        "address_correct": true
      },
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:32:20.000Z",
      "calls": [
        {
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "attempt": 1,
          "status": "completed",
          "answeredAt": "2026-10-13T09:31:12.000Z",
          "endedAt": "2026-10-13T09:32:20.000Z",
          "outcome": "confirmed"
        }
      ],
      "actions": [
        {
          "id": "act_q1w3e5r7t9y1u3i5o7p9",
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "runId": "run_8hq2x2m4q8w1z7p5n6b0",
          "name": "confirm_order",
          "args": {
            "order_id": "4821"
          },
          "status": "success",
          "result": {
            "confirmed": true
          },
          "say": null,
          "errorCode": null,
          "httpStatus": 200,
          "durationMs": 412,
          "createdAt": "2026-10-13T09:31:48.000Z",
          "finishedAt": "2026-10-13T09:31:48.412Z"
        }
      ]
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2"
  }
}

A run gave up

POSTrun.failed → your URL

The run stopped without reaching the shopper; run.lastError says why.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.failed

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.failed",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "gave_up",
      "attempts": 3,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": "no_answer",
      "outcome": null,
      "answers": {},
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:31:02.000Z",
      "calls": [],
      "actions": []
    }
  }
}

A run was cancelled

POSTrun.canceled → your URL

Someone cancelled the run before it finished.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofrun.canceled

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "run.canceled",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "canceled",
      "attempts": 0,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": null,
      "answers": {},
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:31:02.000Z",
      "calls": [],
      "actions": []
    }
  }
}

An action succeeded

POSTaction.completed → your URL

The agent used an action during a call and it succeeded.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofaction.completed

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 3 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

    • actionActionCallrequired
      Show 13 fields inside
      • idstringrequired
      • callIdstringrequired
      • runIdstring or nullrequired
      • namestringrequired
      • argsmap of stringrequired
      • statusstringrequired

        sample: not registered, so the catalogue’s sample reply was used.

        One ofsentsuccessrefusedpendingtimeoutsample

      • resultmap of any or nullrequired
      • saystring or nullrequired
      • errorCodestring or nullrequired
      • httpStatusinteger or nullrequired
      • durationMsinteger or nullrequired
      • createdAtstringrequired

        ISO 8601 time.

      • finishedAtstring or nullrequired

        ISO 8601 time.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "action.completed",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "done",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": "confirmed",
      "answers": {
        "placed_order": true,
        "address_correct": true
      },
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:32:20.000Z",
      "calls": [
        {
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "attempt": 1,
          "status": "completed",
          "answeredAt": "2026-10-13T09:31:12.000Z",
          "endedAt": "2026-10-13T09:32:20.000Z",
          "outcome": "confirmed"
        }
      ],
      "actions": [
        {
          "id": "act_q1w3e5r7t9y1u3i5o7p9",
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "runId": "run_8hq2x2m4q8w1z7p5n6b0",
          "name": "confirm_order",
          "args": {
            "order_id": "4821"
          },
          "status": "success",
          "result": {
            "confirmed": true
          },
          "say": null,
          "errorCode": null,
          "httpStatus": 200,
          "durationMs": 412,
          "createdAt": "2026-10-13T09:31:48.000Z",
          "finishedAt": "2026-10-13T09:31:48.412Z"
        }
      ]
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
    "action": {
      "id": "act_q1w3e5r7t9y1u3i5o7p9",
      "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
      "runId": "run_8hq2x2m4q8w1z7p5n6b0",
      "name": "confirm_order",
      "args": {
        "order_id": "4821"
      },
      "status": "success",
      "result": {
        "confirmed": true
      },
      "say": null,
      "errorCode": null,
      "httpStatus": 200,
      "durationMs": 412,
      "createdAt": "2026-10-13T09:31:48.000Z",
      "finishedAt": "2026-10-13T09:31:48.412Z"
    }
  }
}

An action failed

POSTaction.failed → your URL

An action was refused, timed out or isn’t connected; action.status and action.errorCode say how.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofaction.failed

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 3 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

    • actionActionCallrequired
      Show 13 fields inside
      • idstringrequired
      • callIdstringrequired
      • runIdstring or nullrequired
      • namestringrequired
      • argsmap of stringrequired
      • statusstringrequired

        sample: not registered, so the catalogue’s sample reply was used.

        One ofsentsuccessrefusedpendingtimeoutsample

      • resultmap of any or nullrequired
      • saystring or nullrequired
      • errorCodestring or nullrequired
      • httpStatusinteger or nullrequired
      • durationMsinteger or nullrequired
      • createdAtstringrequired

        ISO 8601 time.

      • finishedAtstring or nullrequired

        ISO 8601 time.

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "action.failed",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "done",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": "confirmed",
      "answers": {
        "placed_order": true,
        "address_correct": true
      },
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:32:20.000Z",
      "calls": [
        {
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "attempt": 1,
          "status": "completed",
          "answeredAt": "2026-10-13T09:31:12.000Z",
          "endedAt": "2026-10-13T09:32:20.000Z",
          "outcome": "confirmed"
        }
      ],
      "actions": [
        {
          "id": "act_q1w3e5r7t9y1u3i5o7p9",
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "runId": "run_8hq2x2m4q8w1z7p5n6b0",
          "name": "confirm_order",
          "args": {
            "order_id": "4821"
          },
          "status": "success",
          "result": {
            "confirmed": true
          },
          "say": null,
          "errorCode": null,
          "httpStatus": 200,
          "durationMs": 412,
          "createdAt": "2026-10-13T09:31:48.000Z",
          "finishedAt": "2026-10-13T09:31:48.412Z"
        }
      ]
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
    "action": {
      "id": "act_q1w3e5r7t9y1u3i5o7p9",
      "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
      "runId": "run_8hq2x2m4q8w1z7p5n6b0",
      "name": "confirm_order",
      "args": {
        "order_id": "4821"
      },
      "status": "refused",
      "result": null,
      "say": "This order has already shipped.",
      "errorCode": "ALREADY_SHIPPED",
      "httpStatus": 200,
      "durationMs": 412,
      "createdAt": "2026-10-13T09:31:48.000Z",
      "finishedAt": "2026-10-13T09:31:48.412Z"
    }
  }
}

A person is wanted

POSThandoff.requested → your URL

The call should go to a person: the shopper asked for one, or your team took it over.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofhandoff.requested

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 3 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

    • handoffHandoffrequired
      Show 3 fields inside
      • transferIdstring or nullrequired

        null while nobody has been asked yet.

      • reasonstringrequired

        manual: your team took the call over. asked_for_person: the shopper asked for one.

        One ofmanualasked_for_person

      • toobject or nullrequired

        Who the call goes to: a teammate in the dashboard, or a phone number.

        Show 2 fields inside
        • userIdstring or nullrequired
        • phonestring or nullrequired

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "handoff.requested",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "done",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": "confirmed",
      "answers": {
        "placed_order": true,
        "address_correct": true
      },
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:32:20.000Z",
      "calls": [
        {
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "attempt": 1,
          "status": "completed",
          "answeredAt": "2026-10-13T09:31:12.000Z",
          "endedAt": "2026-10-13T09:32:20.000Z",
          "outcome": "confirmed"
        }
      ],
      "actions": [
        {
          "id": "act_q1w3e5r7t9y1u3i5o7p9",
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "runId": "run_8hq2x2m4q8w1z7p5n6b0",
          "name": "confirm_order",
          "args": {
            "order_id": "4821"
          },
          "status": "success",
          "result": {
            "confirmed": true
          },
          "say": null,
          "errorCode": null,
          "httpStatus": 200,
          "durationMs": 412,
          "createdAt": "2026-10-13T09:31:48.000Z",
          "finishedAt": "2026-10-13T09:31:48.412Z"
        }
      ]
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
    "handoff": {
      "transferId": "trf_b1n3m5k7j9h1g3f5d7s9",
      "reason": "asked_for_person",
      "to": {
        "userId": "usr_p2o4i6u8y0t2r4e6w8q0",
        "phone": null
      }
    }
  }
}

A person took the call

POSThandoff.connected → your URL

A teammate or a phone number took the call from the agent.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofhandoff.connected

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 3 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

    • handoffHandoffrequired
      Show 3 fields inside
      • transferIdstring or nullrequired

        null while nobody has been asked yet.

      • reasonstringrequired

        manual: your team took the call over. asked_for_person: the shopper asked for one.

        One ofmanualasked_for_person

      • toobject or nullrequired

        Who the call goes to: a teammate in the dashboard, or a phone number.

        Show 2 fields inside
        • userIdstring or nullrequired
        • phonestring or nullrequired

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "handoff.connected",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "done",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": "confirmed",
      "answers": {
        "placed_order": true,
        "address_correct": true
      },
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:32:20.000Z",
      "calls": [
        {
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "attempt": 1,
          "status": "completed",
          "answeredAt": "2026-10-13T09:31:12.000Z",
          "endedAt": "2026-10-13T09:32:20.000Z",
          "outcome": "confirmed"
        }
      ],
      "actions": [
        {
          "id": "act_q1w3e5r7t9y1u3i5o7p9",
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "runId": "run_8hq2x2m4q8w1z7p5n6b0",
          "name": "confirm_order",
          "args": {
            "order_id": "4821"
          },
          "status": "success",
          "result": {
            "confirmed": true
          },
          "say": null,
          "errorCode": null,
          "httpStatus": 200,
          "durationMs": 412,
          "createdAt": "2026-10-13T09:31:48.000Z",
          "finishedAt": "2026-10-13T09:31:48.412Z"
        }
      ]
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
    "handoff": {
      "transferId": "trf_b1n3m5k7j9h1g3f5d7s9",
      "reason": "asked_for_person",
      "to": {
        "userId": "usr_p2o4i6u8y0t2r4e6w8q0",
        "phone": null
      }
    }
  }
}

A shopper opted out

POSToptout.received → your URL

The shopper asked not to be called again. RowCall won’t call the number until they ask to be.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofoptout.received

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 3 fields inside
    • runRunrequired
      Show 20 fields inside
      • idstringrequired
      • flowIdstringrequired
      • flowVersionIdstringrequired

        The exact published version the run follows.

      • contactIdstring or nullrequired
      • phonestringrequired
      • inputsmap of stringrequired
      • languagestringrequired
      • statusstringrequired

        One ofscheduleddialingdonegave_upcanceled

      • attemptsintegerrequired
      • maxAttemptsintegerrequired
      • nextAttemptAtstring or nullrequired

        ISO 8601 time.

      • lastErrorstring or nullrequired

        Why it’s waiting or stopped without reaching the shopper.

      • outcomestring or nullrequired

        The run’s outcome once it ends (its last attempt’s).

      • answersmap of string or number or boolean or nullrequired

        The last attempt’s answers, by question key.

      • metadatamap of stringrequired
      • isTestbooleanrequired
      • createdAtstringrequired

        ISO 8601 time.

      • updatedAtstringrequired

        ISO 8601 time.

      • callslist of RunAttemptrequired
        Show 6 fields inside
        • callIdstringrequired
        • attemptintegerrequired
        • statusstringrequired
        • answeredAtstring or nullrequired

          ISO 8601 time.

        • endedAtstring or nullrequired

          ISO 8601 time.

        • outcomestring or nullrequired
      • actionslist of ActionCallrequired

        Every action its calls asked for, oldest first.

        Show 13 fields inside
        • idstringrequired
        • callIdstringrequired
        • runIdstring or nullrequired
        • namestringrequired
        • argsmap of stringrequired
        • statusstringrequired

          sample: not registered, so the catalogue’s sample reply was used.

          One ofsentsuccessrefusedpendingtimeoutsample

        • resultmap of any or nullrequired
        • saystring or nullrequired
        • errorCodestring or nullrequired
        • httpStatusinteger or nullrequired
        • durationMsinteger or nullrequired
        • createdAtstringrequired

          ISO 8601 time.

        • finishedAtstring or nullrequired

          ISO 8601 time.

    • callIdstring

      The call it’s about.

    • phonestringrequired

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "optout.received",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "run": {
      "id": "run_8hq2x2m4q8w1z7p5n6b0",
      "flowId": "flw_k3j9x2m4q8w1z7p5n6b0",
      "flowVersionId": "flv_z2x4c6v8b0n2m4l6k8j0",
      "contactId": "con_m5n7b9v1c3x5z7l9k1j3",
      "phone": "+919812345678",
      "inputs": {
        "customer_name": "Asha",
        "phone": "+919812345678",
        "order_id": "4821",
        "items": "a pair of running shoes",
        "order_total": "2499",
        "address": "12 MG Road, Pune"
      },
      "language": "hi-IN",
      "status": "done",
      "attempts": 1,
      "maxAttempts": 3,
      "nextAttemptAt": null,
      "lastError": null,
      "outcome": "confirmed",
      "answers": {
        "placed_order": true,
        "address_correct": true
      },
      "metadata": {
        "shopify_order_id": "5523981"
      },
      "isTest": false,
      "createdAt": "2026-10-13T09:31:02.000Z",
      "updatedAt": "2026-10-13T09:32:20.000Z",
      "calls": [
        {
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "attempt": 1,
          "status": "completed",
          "answeredAt": "2026-10-13T09:31:12.000Z",
          "endedAt": "2026-10-13T09:32:20.000Z",
          "outcome": "confirmed"
        }
      ],
      "actions": [
        {
          "id": "act_q1w3e5r7t9y1u3i5o7p9",
          "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
          "runId": "run_8hq2x2m4q8w1z7p5n6b0",
          "name": "confirm_order",
          "args": {
            "order_id": "4821"
          },
          "status": "success",
          "result": {
            "confirmed": true
          },
          "say": null,
          "errorCode": null,
          "httpStatus": 200,
          "durationMs": 412,
          "createdAt": "2026-10-13T09:31:48.000Z",
          "finishedAt": "2026-10-13T09:31:48.412Z"
        }
      ]
    },
    "callId": "cal_r4t6y8u0i2o4p6a8s0d2",
    "phone": "+919812345678"
  }
}

A test event

POSTping → your URL

Sent only by “Send a test event” in Developers → Webhooks, to check your endpoint.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofping

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 1 fields inside
    • messagestringrequired

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "ping",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "message": "A test event from RowCall. Verify its signature, then answer with any 2xx."
  }
}

A merchant connected your app

POSTapp.installed → your URL

Apps only, always sent: a merchant connected your app to their org with these scopes.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofapp.installed

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • appobjectrequired
      Show 2 fields inside
      • idstringrequired
      • namestringrequired
    • scopeslist of stringrequired

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "app.installed",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "app": {
      "id": "app_g7f5d3s1a9p7o5i3u1y9",
      "name": "Glood"
    },
    "scopes": [
      "runs",
      "flows",
      "actions"
    ]
  }
}

A merchant disconnected your app

POSTapp.uninstalled → your URL

Apps only, always sent: the merchant disconnected your app, and its tokens for the org stop working.

Headers

  • RowCall-Signaturestringrequired

    t=<unix seconds>,v1=<hex>: HMAC-SHA256 of <t>.<raw body> with your signing secret. Check it, and that t is within 5 minutes, before trusting the body. During a secret rotation there are two v1 values.

What RowCall sends

  • idstringrequired

    Unique per event: use it to ignore a repeat delivery.

  • typestringrequired

    One ofapp.uninstalled

  • modestringrequired

    One oflivetest

  • orgIdstringrequired

    The org it’s about (an app hears every connected org at one URL).

  • createdAtstringrequired

    ISO 8601 time.

  • dataobjectrequired
    Show 2 fields inside
    • appobjectrequired
      Show 2 fields inside
      • idstringrequired
      • namestringrequired
    • scopeslist of stringrequired

Your answer: 2XX

Received. Anything else is retried with backoff for 24 hours.

Receive it
import { verifyWebhook } from '@rowcall/sdk'
import express from 'express'

const app = express()

app.post('/rowcall/events', express.raw({ type: 'application/json' }), (req, res) => {
  const body = req.body.toString()
  const isGenuine = verifyWebhook({
    body,
    signature: req.get('RowCall-Signature') ?? '',
    secret: process.env.ROWCALL_SIGNING_SECRET,
  })
  if (!isGenuine) return res.sendStatus(401)
  const event = JSON.parse(body)
  // Ignore an event.id you have already handled.
  res.sendStatus(200)
})
Example
{
  "id": "evt_c8v6b4n2m0l8k6j4h2g0",
  "type": "app.uninstalled",
  "mode": "live",
  "orgId": "org_t1y3u5i7o9p1a3s5d7f9",
  "createdAt": "2026-10-13T09:32:20.000Z",
  "data": {
    "app": {
      "id": "app_g7f5d3s1a9p7o5i3u1y9",
      "name": "Glood"
    },
    "scopes": [
      "runs",
      "flows",
      "actions"
    ]
  }
}