Graine AI

Events

Move a live call from your own server. When something happens outside the call, such as a booking made on a web page, send an event and the flow reacts at once, without waiting for the caller to speak.

Most paths are taken because of something the caller said. An event path is taken because of something your own system saw: the caller booked a slot on a web page, finished a form in your app, or a colleague picked up their case. Your server sends a named event into the live call; if the step the call is on has a path waiting for that name, the call takes it, and the step it lands on speaks straight away.

Add an event path

Event paths have no form in the builder. Add them as text in Settings › Advanced › Raw flow, among the step's paths (its next), or through the Flow API:

{ "to": "booked", "label": "Booked", "when": { "type": "event", "event": "slot_booked" } }

event is the name your server will send. A step can wait for several events, each on its own path, alongside its other paths. Once added, the path shows on the canvas like any other, and its panel shows the event it waits for. A branch can't have an event path, because a call never rests on one.

Here is a whole flow built around two events. The agent texts the caller a booking page and stays on the line while they pick a time for a demo.

Booking a demo while the caller stays on the line
{
  "schema_version": 1,
  "start_step": "send_page",
  "briefing": "You are Maya from {company_name}, helping {first_name} book a product demo while they stay on the line. The booking page shows the open times; never promise a time yourself.",
  "decision": { "provider": "azure", "model": "azure/gpt-4.1-mini" },
  "steps": {
    "send_page": {
      "kind": "converse",
      "title": "Send the booking page",
      "instructions": "Tell {first_name} you've texted them a booking page. Ask them to open it and pick a time while you stay on the line, and answer any questions about the demo.",
      "next": [
        { "to": "booked", "label": "Booked", "when": { "type": "event", "event": "slot_booked" } },
        { "to": "taken", "label": "Time gone", "when": { "type": "event", "event": "slot_taken" } },
        {
          "to": "by_voice",
          "label": "Taking a while",
          "when": {
            "type": "rule",
            "match": "all",
            "rules": [{ "field": "_step_turns", "op": "at_least", "value": 4 }]
          }
        }
      ]
    },
    "booked": {
      "kind": "say",
      "title": "Confirm the booking",
      "say": { "en": "You're booked for {booked_slot}. The invitation is on its way. Thanks, and speak soon!" },
      "force_tool": "end_call",
      "next": []
    },
    "taken": {
      "kind": "converse",
      "title": "That time went",
      "instructions": "Explain that {requested_slot} was taken a moment ago, and ask them to pick another time on the same page.",
      "next": [
        { "to": "booked", "label": "Booked", "when": { "type": "event", "event": "slot_booked" } },
        { "to": "by_voice", "label": "Rather say it", "when": { "type": "ai", "description": "The caller would rather tell you a time than use the page." } }
      ]
    },
    "by_voice": {
      "kind": "converse",
      "title": "Book by voice",
      "instructions": "Ask which day and time suits them and say the team will confirm it by email. Thank them and say goodbye.",
      "force_tool": "end_call",
      "next": []
    }
  }
}

While the call is on Send the booking page, the agent keeps talking with the caller; nothing they say moves the call on, except the rule that gives up on the page after four turns. When your server sends slot_booked with booked_slot in its data, the call moves to Confirm the booking, which says the time at once and hangs up after its line. If the time was taken first, slot_taken (with requested_slot) moves the call to That time went, which waits for the next booking.

Send an event

POST https://api.graine.ai/v2/calls/{execution_id}/events

Send it from your own server, with an API key (Authorization: Bearer gat_…) that can place calls (the calls:write scope). execution_id is the id the call was placed under, the one POST /v2/calls returns; the call must be one of your organization's.

curl -X POST "https://api.graine.ai/v2/calls/$EXECUTION_ID/events" \
  -H "Authorization: Bearer $GRAINE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "slot_booked", "data": {"booked_slot": "Thursday at 3 pm"}}'

Body

FieldType
nametext, requiredThe event's name, up to 64 characters. It is what an event path's event matches.
dataobject, optionalValues that join the call's data under their own names, up to 8 KB as JSON. Defaults to {}.

Responses

202 Accepted means the event was queued for the call:

{ "accepted": true, "name": "slot_booked", "execution_id": "8f2a1c7e-5d34-4b19-9c60-1a7f0b2e4d88" }

It does not mean a path was taken: that depends on the step the call is on when the event is handled (below).

Errors use the /v2 envelope, {"error": <code>, "message": "<sentence>"}:

StatuserrorWhen
202—Queued.
4011100No API key, or one that isn't valid.
4031102The key can't place calls.
4041200No call with that id in your organization.
4091201The call isn't live: it is scheduled and hasn't been placed yet, it has already ended, or it is still connecting. The last answer carries Retry-After: send the event again after that many seconds.
4221001name is missing, empty or longer than 64 characters; data isn't an object or is larger than 8 KB; or the body has a field this endpoint doesn't take.
4291300Rate limited. Retry after Retry-After seconds.
5031501Temporarily unavailable. Retry with backoff.

What the call does with it

  1. The call picks the event up within about a second, then waits until the agent is neither speaking nor working on a reply, for up to 30 seconds, so it never talks over itself.

  2. Its data joins the call's data under the names you sent, and the event's name is kept as _last_event. Rules can test the values (booked_slot is filled in) and text can say them ({booked_slot}). See Values picked up during the call.

  3. If the current step has an event path for that name, the call takes it, and the step it lands on speaks at once:

    • an AI reply speaks first, without waiting for the caller;
    • a fixed line plays its line, and hangs up afterwards if it is set to;
    • a branch decides and moves the call on to a step that speaks.

    If the caller happens to be talking at that moment, the call still moves, but the new step answers what the caller says instead of speaking first.

  4. If it has no such path, nothing is said. The data is still added, for later steps and rules.

Events for one call are handled one at a time, in the order they arrive. When two event paths on a step wait for the same name, the one with the lower order is taken. An event never puts words in the caller's mouth: the transcript shows the agent's new turn straight after its previous one.

Trying an event path

Events need a live call with an id you know. Place one with POST /v2/calls, which answers with its execution_id, and send the event from a terminal while you're on the call.

Next: How a call moves through a flow.

On this page