Graine AI

Fetch one execution

Fetch one execution by id.

Fetch one execution by id.

The same id answers for the call's whole life: while it is still scheduled (before any dial), while it is ringing or in progress, and after it reaches a terminal status. A client never has to branch on which phase it is in — read status and is_terminal.

A call belonging to another organization answers 404, exactly like an id that does not exist: confirming that someone else's execution id is real would leak more than it is worth.

The id in the response can differ from the id you asked for. In the rare case where one call ends up with two records, the platform merges them into the record of the call that actually happened. Your original id keeps resolving — this endpoint follows the merge — but the body's execution_id is then the id of the record that was kept. Treat the response's execution_id as canonical, store it, and do not assert that it equals the id you requested. List endpoints never show the same call twice.

Rate limit: 1200 requests per minute per organization, shared by the endpoints that read calls — lists, single calls, transcripts, recordings and timing — and usage. Exceeding it returns 429 with Retry-After; the X-RateLimit-* response headers report your remaining allowance on every call.

GET
/v2/calls/{execution_id}
/v2/calls/dbcffa37-92a1-4ae7-9814-71ca7b8ab3d3?include_transcript=false

The Authorization access token

Authorization

Authorization
Required
Bearer <token>

Your Graine API key. Create one in the dashboard under Developers, or via POST /v2/api-keys. Send it as Authorization: Bearer <key>.

In: header

Path Parameters

execution_id
Required
Execution Id

The call's id, as returned by POST /v2/calls or GET /v2/calls.

Query Parameters

include_transcriptInclude Transcript

Include the full transcript.

Default: false

organization_idOrganization Id

Optional. Must equal your key's organization.

Response Body

200

The execution, whatever phase it is in.

execution_idExecution Id | null

The id this call runs under for its whole life.

statusStatus | null

Lifecycle status: queued, scheduled, rescheduled, initiated, ringing, in-progress, completed, failed, busy, no-answer, canceled, stopped, balance-low, error, call-disconnected or do_not_call. rescheduled and stopped are kept for compatibility and no call placed today is stored with either: a call its calling window moves stays scheduled, and a call you stop settles in the status recorded when it hangs up. A call the voice agent never took is failed, with an error message that begins agent_unavailable.

is_terminalIs Terminal

True once the call has reached a status it will never leave.

Default: false

directionDirection | null

inbound or outbound.

agent_idAgent Id | null

The agent that ran the call.

batch_idBatch Id | null

The batch that placed it, if any.

campaign_idCampaign Id | null

The campaign the batch belongs to, if any.

contact_idContact Id | null

The contact row dialled, if any. Null on a call you placed directly.

parent_execution_idParent Execution Id | null

The original attempt, when this call is a retry or a follow-up. Null on a first attempt.

retry_countRetry Count

Attempts before this one.

Default: 0

to_numberTo Number | null

Recipient.

from_numberFrom Number | null

Caller ID used.

providerProvider | null

Always "custom". /v2 does not name the carrier.

duration_secondsDuration Seconds | null

Connected audio, in seconds.

answered_by_voicemailAnswered By Voicemail | null

True when a voicemail box picked up instead of a person.

hangup_byHangup By | null

Which side ended the call: Caller (the person called hung up), Callee (no answer, busy or a machine), Agent (the voice agent ended it, or never took it), Provider (the network failed or cancelled it), Transfer (handed to a person) or system (you stopped it). Display text, passed through unchanged; do not branch on it.

hangup_reasonHangup Reason | null

Why the call ended, in words — not a fixed vocabulary. The one machine-readable case is a call the voice agent never took, whose reason begins agent_unavailable.

recording_urlRecording Url | null

Audio of the call. Null when nothing was recorded.

costCost

What the call cost.

summarySummary | null

Model-written summary of the conversation.

sentimentSentiment | null

positive, neutral or negative.

extracted_dataExtracted Data | null

The variables your agent's analysis configuration asked for, keyed by variable name.

custom_analyticsCustom Analytics | null

The analysis configuration that produced 'extracted_data'.

language_switchingLanguage Switching

Which languages the call spoke and when the agent switched between them. Null on a call that stayed in one language.

variablesVariables

The variables this call ran with: the variables you sent, with metadata merged over them — readable from the moment the call is created. Once the call reports, the platform adds context keys of its own (for example agent_name or call_type); read the keys you sent and ignore the rest.

errorError

Why the call failed, when a reason was recorded; null otherwise. A call the voice agent never took carries a message beginning agent_unavailable — match that prefix, not the prose after it.

scheduled_atScheduled At | null

When the call is due to be placed. Null on a call that was dialled immediately.

created_atCreated At | null

When the call was created.

updated_atUpdated At | null

Last change to this record.

completed_atCompleted At | null

When the call reached a terminal status.

transcriptTranscript

Present only when include_transcript=true. Either an array of {role, content} turns or a plain string, depending on how the call was recorded. GET /v2/calls/{execution_id}/transcript normalises both into one shape.

401

1100 — missing, unknown or inactive API key. 1101 — a browser session token was presented instead of an API key.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

403

1102 — the key is valid but lacks the scope this endpoint requires, or names another organization. GET /v2/scopes reports what a key holds.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

404

1200 — no such resource in your organization. A resource belonging to another organization answers 404, never 403.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

422

1001 — the body or query failed validation; the message names the first offending field.

error
Required
integer

Stable integer code from the error table. Branch on this, not on the message.

message
Required
string

One human-readable sentence. Wording may change; the code will not.

429

1300 — the per-organization request rate or concurrent-call limit was reached. Carries Retry-After.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

503

1501 — a service this endpoint depends on is unreachable or returned a 5xx.

error
Required
Error

Stable integer code from the /v2 error table. Branch on this.

message
Required
Message

One human-readable sentence. Wording may change; the code will not.

curl -X GET "https://api.graine.ai/v2/calls/dbcffa37-92a1-4ae7-9814-71ca7b8ab3d3?include_transcript=false&organization_id=string" \
  -H "Authorization: Bearer <token>"

The execution, whatever phase it is in.

{
  "execution_id": "dbcffa37-92a1-4ae7-9814-71ca7b8ab3d3",
  "status": "completed",
  "is_terminal": true,
  "direction": "outbound",
  "agent_id": "6c6f4cc3-f56c-479d-8422-7f76694daa29",
  "batch_id": "7d1f0a52-1a2b-4c3d-9e8f-0a1b2c3d4e5f",
  "campaign_id": "31ff3ea9-8bb5-433f-868f-286ac3c31328",
  "contact_id": "c41f7e02-9b8a-4d51-b0c7-6f2a1e3d4c55",
  "parent_execution_id": "9b2c1d84-77e3-4a10-8f5b-2c6d9e0a1b33",
  "retry_count": 0,
  "to_number": "+919812345678",
  "from_number": "+917971442184",
  "provider": "custom",
  "duration_seconds": 209,
  "answered_by_voicemail": false,
  "hangup_by": "Caller",
  "hangup_reason": "User disconnected the call",
  "recording_url": "https://storage.graine.ai/2026/08/26/0d76fa8c-ea49-4d78-9e5b-1a15ab7b3ac2.mp3",
  "cost": {
    "total": 0.134068,
    "currency": "USD",
    "breakdown": {
      "llm": 0.020814,
      "network": 0,
      "platform": 0.01,
      "synthesizer": 0.052034,
      "telephony": 0.02,
      "transcriber": 0.03122
    }
  },
  "summary": "Dealer was unaware of the campaign; the cashback process was explained.",
  "sentiment": "neutral",
  "extracted_data": {
    "call_outcome": "already_knew_no_doubts",
    "follow_up": false,
    "callback_time_requested": ""
  },
  "custom_analytics": [
    {
      "name": "Custom Analytics",
      "required_keys": [
        "call_outcome"
      ]
    }
  ],
  "language_switching": {
    "starting_language": "hi",
    "final_language": "en",
    "switch_count": 1,
    "switches": [
      {
        "at_seconds": 42.4,
        "from_language": "hi",
        "to_language": "en",
        "trigger": "asked"
      }
    ]
  },
  "variables": {
    "callee_name": "Raju",
    "agent_name": "Priya Plumber Activation Agent"
  },
  "error": {
    "message": "The recipient's carrier rejected the call."
  },
  "scheduled_at": "2026-08-27T09:30:00.000Z",
  "created_at": "2026-08-26T12:19:54.580Z",
  "updated_at": "2026-08-26T12:25:01.543Z",
  "completed_at": "2026-08-26T12:23:35.194Z",
  "transcript": [
    {
      "role": "assistant",
      "content": "Namaste, main Kavya bol rahi hoon."
    },
    {
      "role": "user",
      "content": "Haan boliye."
    }
  ]
}