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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.queuedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.ringingmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.answeredmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.no_answermodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.completedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.failedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
run.canceledmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
action.completedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 3 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
actionActionCallrequiredShow 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
action.failedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 3 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
actionActionCallrequiredShow 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
handoff.requestedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 3 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
handoffHandoffrequiredShow 3 fields insideHide the fields inside
transferIdstring or nullrequirednull while nobody has been asked yet.
reasonstringrequiredmanual: your team took the call over.asked_for_person: the shopper asked for one.One of
manualasked_for_persontoobject or nullrequiredWho the call goes to: a teammate in the dashboard, or a phone number.
Show 2 fields insideHide the fields inside
userIdstring or nullrequiredphonestring or nullrequired
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
handoff.connectedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 3 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
handoffHandoffrequiredShow 3 fields insideHide the fields inside
transferIdstring or nullrequirednull while nobody has been asked yet.
reasonstringrequiredmanual: your team took the call over.asked_for_person: the shopper asked for one.One of
manualasked_for_persontoobject or nullrequiredWho the call goes to: a teammate in the dashboard, or a phone number.
Show 2 fields insideHide the fields inside
userIdstring or nullrequiredphonestring or nullrequired
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
optout.receivedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 3 fields insideHide the fields inside
runRunrequiredShow 20 fields insideHide the fields inside
idstringrequiredflowIdstringrequiredflowVersionIdstringrequiredThe exact published version the run follows.
contactIdstring or nullrequiredphonestringrequiredinputsmap of stringrequiredlanguagestringrequiredstatusstringrequiredOne of
scheduleddialingdonegave_upcanceledattemptsintegerrequiredmaxAttemptsintegerrequirednextAttemptAtstring or nullrequiredISO 8601 time.
lastErrorstring or nullrequiredWhy it’s waiting or stopped without reaching the shopper.
outcomestring or nullrequiredThe run’s outcome once it ends (its last attempt’s).
answersmap of string or number or boolean or nullrequiredThe last attempt’s answers, by question key.
metadatamap of stringrequiredisTestbooleanrequiredcreatedAtstringrequiredISO 8601 time.
updatedAtstringrequiredISO 8601 time.
callslist of RunAttemptrequiredShow 6 fields insideHide the fields inside
callIdstringrequiredattemptintegerrequiredstatusstringrequiredansweredAtstring or nullrequiredISO 8601 time.
endedAtstring or nullrequiredISO 8601 time.
outcomestring or nullrequired
actionslist of ActionCallrequiredEvery action its calls asked for, oldest first.
Show 13 fields insideHide the fields inside
idstringrequiredcallIdstringrequiredrunIdstring or nullrequirednamestringrequiredargsmap of stringrequiredstatusstringrequiredsample: not registered, so the catalogue’s sample reply was used.One of
sentsuccessrefusedpendingtimeoutsampleresultmap of any or nullrequiredsaystring or nullrequirederrorCodestring or nullrequiredhttpStatusinteger or nullrequireddurationMsinteger or nullrequiredcreatedAtstringrequiredISO 8601 time.
finishedAtstring or nullrequiredISO 8601 time.
callIdstringThe call it’s about.
phonestringrequired
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
pingmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 1 fields insideHide the fields inside
messagestringrequired
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
app.installedmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
appobjectrequiredShow 2 fields insideHide the fields inside
idstringrequirednamestringrequired
scopeslist of stringrequired
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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-Signaturestringrequiredt=<unix seconds>,v1=<hex>: HMAC-SHA256 of<t>.<raw body>with your signing secret. Check it, and thattis within 5 minutes, before trusting the body. During a secret rotation there are twov1values.
What RowCall sends
idstringrequiredUnique per event: use it to ignore a repeat delivery.
typestringrequiredOne of
app.uninstalledmodestringrequiredOne of
livetestorgIdstringrequiredThe org it’s about (an app hears every connected org at one URL).
createdAtstringrequiredISO 8601 time.
dataobjectrequiredShow 2 fields insideHide the fields inside
appobjectrequiredShow 2 fields insideHide the fields inside
idstringrequirednamestringrequired
scopeslist of stringrequired
Your answer: 2XX
Received. Anything else is retried with backoff for 24 hours.
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)
}){
"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"
]
}
}