phone-call
The phone-call skill enables outbound AI voice calls through Bland AI to complete tasks like booking reservations, scheduling appointments, or gathering information from businesses. Use it when you need an AI agent to conduct a live phone conversation, collect responses, and return a transcript and summary without manual dialing.
git clone --depth 1 https://github.com/Lilac-Labs/gini-agent /tmp/phone-call && cp -r /tmp/phone-call/skills/integrations/phone-call ~/.claude/skills/phone-callSKILL.md
# Phone Call
Place outbound phone calls through Bland AI. An AI voice agent dials the number, follows your task prompt in a live conversation, and the full transcript plus a summary come back when the call ends. Everything runs through `skill_run`:
```
skill_run({ skill: "phone-call", script: "place-call",
args: { phoneNumber: "+15551234567", task: "..." } })
```
The runtime injects `BLAND_API_KEY` into the scripts — you never see or pass the key.
## Workflow
1. **Gather the full task.** Before anything else, collect: who to call (name + number), the goal, hard constraints (dates, times, party size, budget), fallbacks if the first ask isn't available, and the name to give if the callee asks who's calling. A vague task produces a bad call.
2. **Announce, then dial — the approval card is the confirmation.** In one short message, state who you're calling (the exact number) and what the agent will say or ask, then call `place-call` immediately. Do not ask "should I go ahead?" in chat: `place-call` always pauses on an Approve/Deny card, and approving that card is the user's go-ahead. A denied card ends the task; if the user sends a new message with changes, adjust per their feedback and submit a fresh `place-call` — never re-submit the identical call.
3. **Place the call** with `place-call`. It returns `{ ok, callId }`.
4. **Wait for the result** with `check-call`, passing `waitSeconds: 240` — the script polls Bland every 10 seconds internally and returns as soon as the call completes (or when the budget runs out). If the result comes back with `completed: false`, call `check-call` again with the same args and repeat until `completed` is `true`.
5. **Report back** the `summary` and the key points of the `transcript` (quote relevant exchanges, don't dump the whole thing unless asked). Optional: for structured answers about the call (did they confirm? what time?), run `analyze-call`.
If the user wants to abort a call in progress, run `stop-call` with the `callId`.
## Scripts
### place-call
```
skill_run({ skill: "phone-call", script: "place-call", args: {
phoneNumber: "+15551234567", // required, E.164
task: "...", // required, the call prompt
voice: "maya", // optional, Bland voice id/name
firstSentence: "...", // optional, exact opening line
waitForGreeting: true, // default true — wait for the callee to speak first
record: false, // default false — see Rules
maxDurationMinutes: 10, // default 10
language: "en-US" // optional
} })
```
Returns `{ ok, callId }` or `{ ok: false, error }`.
### check-call
```
skill_run({ skill: "phone-call", script: "check-call", args: { callId: "...", waitSeconds: 240 } })
```
Returns `{ ok, callId, status, completed, answeredBy, callLengthMinutes, to, from, transcript, summary, recordingUrl, errorMessage }`. `waitSeconds` (optional, default 0, max 240) makes the script wait for the call to finish, polling Bland every 10 seconds; if the budget runs out first it returns the latest status with `completed: false` — call again with the same args. Fields Bland hasn't populated yet are omitted; `transcript` and `summary` appear only after `completed` is `true`. `callLengthMinutes` is in minutes. `answeredBy` distinguishes `human` from `voicemail`.
### stop-call
```
skill_run({ skill: "phone-call", script: "stop-call", args: { callId: "..." } })
```
Ends an in-progress call. Returns `{ ok, message }`.
### analyze-call
```
skill_run({ skill: "phone-call", script: "analyze-call", args: {
callId: "...", // required
goal: "Book a dinner reservation", // optional context
questions: [ // required, non-empty
["Did they confirm the reservation?", "boolean"],
["What time was booked?", "string"]
]
} })
```
Use after `check-call` reports `completed: true` when you need structured answers instead of reading the transcript — e.g. "Did they confirm the reservation?" → `true`. Each question is a `[question, answerType]` pair (`"string"`, `"boolean"`, `"number"`); a bare string question defaults to `"string"`. Returns `{ ok, answers }` with one answer per question, in order. Each analysis costs Bland credits, so prefer the transcript/summary for simple cases.
## Background watching
The workflow above is synchronous — right for short errand calls where the user is waiting on the result. When the user doesn't want to wait, or the call may run long, hand the waiting to a scheduled job with a `call-watch` pre-run hook: the hook polls Bland with zero model turns while the call is in progress, and wakes the job's turn exactly once, when the call finishes.
1. Place the call with `place-call` as usual and note the `callId`.
2. Create the watcher job:
```
create_job({
name: "call-watch <callId>",
intervalSeconds: 30,
oneShot: false, // must stay false — the silent in-progress ticks would auto-pause a one-shot job before the call finishes
timeoutSeconds: 120,
preRunHook: {
handlerId: "skill-script",
config: { skill: "phone-call", script: "call-watch", callId: "<callId>" }
},
prompt: "A phone call placed in the background has finished. The call result (status, summary, transcript) is in the fenced context items above — report the summary and the key transcript exchanges. Then find this job by name with list_jobs and delete_job it; the call is done and the watcher is no longer needed."
})
```
3. Tell the user the call is underway and the result will arrive in the job's chat thread, then end your turn — do not poll with `check-call`.
Notes:
- The report lands in the job's own chat thread (scheduled jobs deliberately do not post into the main chat).
- `call-watch` is the job's hook script, not for direct `skill_run` use. A failed call (never answered, rejected) also wakes the turn, so the user always hears the outcome.
##Delegate coding work to Claude Code CLI for repository edits, reviews, and multi-turn implementation sessions.
Delegate coding work to the OpenAI Codex CLI for repository changes, reviews, and focused fixes.
Gini's self-knowledge: how Gini configures, extends, and operates on its own state via /api/* and registered tools. Load when the user asks Gini about its own capabilities or asks Gini to modify its own configuration.
Manage Apple Notes via memo CLI: create, search, edit.
Apple Reminders via remindctl: add, list, complete.
Move bytes between Gini upload space, external URLs, and workspace files. Used by every attachment / file-upload / file-download flow regardless of the target system (Linear, GitHub, S3, Notion, etc.).
File a locally-captured, already-redacted Gini crash report as a GitHub issue, with the user's consent. Reads the pending crash queue and delegates the actual filing to the github-issues skill.
Create, search, triage, label, assign, comment on, and close GitHub issues using the gh CLI, with a curl REST fallback.