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

Join the waitlist

Search the docs

Find a docs page

API reference

Start a run

Preview

Start 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.

POST /v1/runs, then the run goes scheduled → dialing → done, and run.completed reaches your webhook.

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 the flowKey of a library flow installed for the org, such as order_verification. Not both.
  • Who to call. A phone (any common Indian format, saved as +91…), or the contactId of a contact you saved earlier. Not both. name saves the number as a contact.
  • The inputs. The facts the flow reads to the shopper, by key, as text. The flow's inputSchema says which are required. An input you leave out is taken from the contact's field of the same name, and customer_name from the contact's name.
  • Why you may call. consent records the shopper's consent on their contact before the call; transactionAt lets 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:

  1. The flow is published (NOT_PUBLISHED).
  2. The number hasn't opted out (OPTED_OUT).
  3. Consent, or a recent purchase, allows the flow's call type (NO_CONSENT).
  4. The flow's required inputs are there (INVALID_INPUT, naming the input).
  5. It's inside calling hours, when you sent whenClosed: "refuse" (OUTSIDE_CALLING_HOURS).
  6. 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

  1. Queued

    You get run.queued. Inside calling hours the run dials at once; outside them it waits, scheduled, until they open (unless you sent whenClosed: "refuse").

  2. The call

    RowCall dials, opens with the disclosure line and follows the flow. You get run.ringing and run.answered, and action.completed or action.failed for each action the call uses.

  3. The outcome

    When an answered call ends you get run.completed, carrying the run with its outcome, answers and actions. An unanswered attempt sends run.no_answer and is retried after the flow's gap; when attempts run out you get run.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.

Next stepWebhooks

Receive the results as they happen.