agentleFS
thunderphonethunderphone/skills/place-outbound-calls/SKILL.md

place-outbound-calls

Skill0 starsChanged 58 days agoMITView on GitHub

Place, monitor, or troubleshoot an outbound ThunderPhone call. Use when someone asks about POST /v1/call, TCPA confirmation, insufficient balance, caller and recipient numbers, call variables, idempotency, polling status, transcripts, or when to use a campaign.

  • Reads credentials
  • Sends data out

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 4 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Poll and inspect: Use GET /v1/calls/{call_id} until terminal, then retrieve GET /v1/calls/{call_id}/transcript.

    line 75
  2. Very specific:

    Switch to campaigns for volume: Use the run-campaign skill for contact imports, calling windows, pause/cancel controls, and aggregate …

    line 77
  3. Distinctive:

    Call in the contact's local hours, and retry on another day at another time, not on a short loop.

    line 86
  4. Very specific:

    Read answered_by on finished calls and watch GET /v1/phone-numbers/health.

    line 91
  5. Distinctive:

    Follow place outbound calls, outbound calling laws, and [recording consent

    line 108
  6. Distinctive:

    Never infer consent, disguise caller identity, or represent initiation as completion.

    line 110
  7. Distinctive:

    Do not include sensitive data in variables unless the conversation requires it.

    line 111

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Place an outbound call

  1. Confirm authority and consent. Identify the recipient, purpose, local time, applicable calling/recording rules, and the evidence supporting the call. Do not infer consent. Check GET /v1/outbound-tcpa-confirmation. Recording the confirmation requires an authenticated organization admin in the dashboard; an API key cannot self-attest around the gate.
  2. Validate resources. The from_number must support outbound calls. Use a deployed agent_id, supply a complete inline config, or omit both to use the number's outbound_agent; omission returns 400 if none is configured. Optional max_hold_seconds overrides the hold timeout for this call. Check balance before a production batch.
  3. Create an idempotency key for each intended call so a retry cannot create a second call. Send per-call facts in variables, not instructions.
  4. Place one controlled call. POST /v1/call requires from_number and to_number; it accepts agent_id, variables, and idempotency_key.
{
  "from_number": "${FROM_NUMBER}",
  "to_number": "${TO_NUMBER}",
  "agent_id": 12,
  "variables": {},
  "max_hold_seconds": 120,
  "idempotency_key": "${IDEMPOTENCY_KEY}"
}
curl --fail-with-body -X POST https://api.thunderphone.com/v1/call \
  -H "Authorization: Bearer $THUNDERPHONE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-ThunderPhone-Client: skills/place-outbound-calls@1.0" \
  --data "$(jq -n --arg from "$FROM_NUMBER" --arg to "$TO_NUMBER" --argjson agent "$AGENT_ID" --arg key "$IDEMPOTENCY_KEY" '{from_number:$from,to_number:$to,agent_id:$agent,idempotency_key:$key}')"
import os, requests

payload = {"from_number": os.environ["FROM_NUMBER"], "to_number": os.environ["TO_NUMBER"],
           "agent_id": int(os.environ["AGENT_ID"]), "idempotency_key": os.environ["IDEMPOTENCY_KEY"]}
r = requests.post("https://api.thunderphone.com/v1/call", json=payload,
    headers={"Authorization": f"Bearer {os.environ['THUNDERPHONE_API_KEY']}",
             "X-ThunderPhone-Client": "skills/place-outbound-calls@1.0"}, timeout=30)
r.raise_for_status()
print(r.json())
const response = await fetch("https://api.thunderphone.com/v1/call", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.THUNDERPHONE_API_KEY}`,
    "Content-Type": "application/json",
    "X-ThunderPhone-Client": "skills/place-outbound-calls@1.0" },
  body: JSON.stringify({ from_number: process.env.FROM_NUMBER, to_number: process.env.TO_NUMBER,
    agent_id: Number(process.env.AGENT_ID), idempotency_key: process.env.IDEMPOTENCY_KEY }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
  1. Interpret acceptance correctly. A 201 means initiation was accepted, not that a person answered or the conversation succeeded. A 202 can mean the provider outcome is not yet known. Preserve the returned call ID.
  2. Poll and inspect. Use GET /v1/calls/{call_id} until terminal, then retrieve GET /v1/calls/{call_id}/transcript. Use webhooks for scale.
  3. Switch to campaigns for volume. Use the run-campaign skill for contact imports, calling windows, pause/cancel controls, and aggregate statistics.

Deliverability checklist

Campaign pacing does not apply to POST /v1/call, so the caller's code does it:

  • Choose a from_number with the contact's area code and reuse it for that person. Keep each number well under 100 calls a day.
  • Call in the contact's local hours, and retry on another day at another time, not on a short loop.
  • Send any heads-up text with POST /v1/sms only to people who agreed to it.
  • Set the agent to leave a voicemail rather than hang up, and give the number an inbound agent so callbacks reach someone.
  • Read answered_by on finished calls and watch GET /v1/phone-numbers/health. Retire a labeled number; never rotate fresh numbers to hide it.

See get outbound calls answered.

Errors

SymptomCauseFix
403 names TCPA confirmationOrganization gate has not been completed by an adminComplete the reviewed dashboard attestation; do not automate around it.
402 insufficient balanceOrganization cannot fund the callTop up through an authorized billing flow, then retry with the same idempotency key only if no call exists.
Number rejectedNumber lacks outbound capability or format is invalidRead the number record and use E.164 formatting.
Duplicate callsRetry used a new or missing idempotency keyPersist one key per intended call and reconcile the first result.

Source and safety rules

See references/outbound-readiness.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.