Start a run
PreviewStart a RowCall call from your systems with one request, and follow it until the answers come back.
A run is one flow for one shopper: RowCall places the call (and any retries), follows the flow, and records the outcome. You start one with POST /v1/runs, using a key with the runs scope. This page walks through one: Halden verifying cash-on-delivery order 4821.
The request
curl -X POST https://app.rowcall.ai/v1/runs \
-H "Authorization: Bearer $ROWCALL_API_KEY" \
-H "Idempotency-Key: verify-4821" \
-H "Content-Type: application/json" \
-d '{
"flowKey": "order_verification",
"phone": "+919812345678",
"name": "Asha Rao",
"inputs": {
"customer_name": "Asha",
"order_id": "4821",
"items": "a pair of running shoes",
"order_total": "2499",
"address": "12 MG Road, Pune"
},
"consent": {
"scope": "service",
"source": "checkout",
"givenAt": "2026-10-13T09:20:00+05:30",
"note": "Ticked \"Call me about this order\" at checkout."
},
"transactionAt": "2026-10-13T09:20:00+05:30",
"metadata": { "shopify_order_id": "5523981" }
}'What each part does:
- Which flow. A
flowId(flw_…, from the Flows API), or theflowKeyof a library flow installed for the org, such asorder_verification. Not both. - Who to call. A
phone(any common Indian format, saved as+91…), or thecontactIdof a contact you saved earlier. Not both.namesaves the number as a contact. - The inputs. The facts the flow reads to the shopper, by key, as text. The flow's
inputSchemasays which are required. An input you leave out is taken from the contact's field of the same name, andcustomer_namefrom the contact's name. - Why you may call.
consentrecords the shopper's consent on their contact before the call;transactionAtlets a transactional flow call within 30 minutes of the purchase without a consent record. See Consent records. - Your references.
metadata(up to 20 keys) is kept on the run and sent back with every event, so you can match results to your order without storing RowCall's ids.
Every field, with its type and limits, is in the reference. Send an Idempotency-Key built from the order, so a retried request never calls twice. See Idempotency.
The same request with the SDK:
const run = await rowcall.runs.start(
{
flowKey: 'order_verification',
phone: '+919812345678',
name: 'Asha Rao',
inputs: {
customer_name: 'Asha',
order_id: '4821',
items: 'a pair of running shoes',
order_total: '2499',
address: '12 MG Road, Pune',
},
consent: {
scope: 'service',
source: 'checkout',
givenAt: '2026-10-13T09:20:00+05:30',
note: 'Ticked "Call me about this order" at checkout.',
},
metadata: { shopify_order_id: '5523981' },
},
{ idempotencyKey: 'verify-4821' },
)The response
RowCall answers 201 with the run. Inside calling hours the first attempt is placed while the request is answered, so the run comes back dialing with attempts: 1; outside them it comes back scheduled, waiting for calling hours to open. Keep run.id; every event about this call carries the run. The full response is in the reference.
The checks
Every check a call needs happens before the run exists. A request that fails one is refused and no run is made, so you can show the reason in your own system at once:
- The flow is published (
NOT_PUBLISHED). - The number hasn't opted out (
OPTED_OUT). - Consent, or a recent purchase, allows the flow's call type (
NO_CONSENT). - The flow's required inputs are there (
INVALID_INPUT, naming the input). - It's inside calling hours, when you sent
whenClosed: "refuse"(OUTSIDE_CALLING_HOURS). - For a live run: RowCall has approved your business (
NOT_VERIFIED), your org has a number for the call type (NO_NUMBER), and your balance covers a minute of calling (NO_CREDIT).
Test runs skip the live-only checks. What each code means and what to do is in the error table.
If every line is busy, the run waits about 15 seconds and tries again, without using an attempt. If the first call can't be placed at all, the request fails with the reason and the run ends as gave_up with run.failed.
What happens next
Queued
You get
run.queued. Inside calling hours the run dials at once; outside them it waits,scheduled, until they open (unless you sentwhenClosed: "refuse").The call
RowCall dials, opens with the disclosure line and follows the flow. You get
run.ringingandrun.answered, andaction.completedoraction.failedfor each action the call uses.The outcome
When an answered call ends you get
run.completed, carrying the run with its outcome, answers and actions. An unanswered attempt sendsrun.no_answerand is retried after the flow's gap; when attempts run out you getrun.failed.
Each attempt checks again for an opt-out or withdrawn consent, so a retry days later never calls someone who said no.
Saving contacts ahead of time
If your store records consent at checkout, save it then with PUT /v1/contacts (contacts scope). It adds the number, or updates the contact that has it, merging the fields you send and recording consent at the same time. Later runs can use the contactId, and inputs you leave out come from the contact's fields.
Cancelling a run
POST /v1/runs/{runId}/cancel stops any further attempts and sends run.canceled. A call already on carries on to its end. A finished run stays as it is.
Receive the results as they happen.