agentleFS
kessleriokesslerio/dialpad-skill/SKILL.md

dialpad

Skill2 starsChanged 52 days agoMITView on GitHub

Send SMS and make voice calls via Dialpad API using task-focused wrappers backed by an OpenAPI-generated CLI.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

8 rules. About 0 are common, 2 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill to:

    line 15
  2. Very specific:

    When DIALPAD_LOG_URL is configured, SMS thread/inbox reads and call-list reads use the authenticated theshop interaction log by default.

    line 75
  3. Very specific:

    Escaping: Use single quotes for inline --message values containing $ to prevent shell expansion (e.g., 'Price is $10').

    line 104
  4. Very specific:

    Safer message input: Prefer --message-file or --message-stdin for pricing text, multi-line copy, or anything shell-sensitive.

    line 105
  5. Very specific:

    Supported agent interface: use bin/*.py wrappers for normal work.

    line 106
  6. Very specific:

    default fallback order: DIALPAD_DEFAULT_FROM_NUMBER, then DIALPAD_DEFAULT_PROFILE

    line 113
  7. Very specific:

    Receipt phrasing: When confirming an SMS send to an operator, always include To: <number> in the receipt text so the downstream guard can …

    line 124
  8. Distinctive:

    Use these only for manual operator workflows, storage inspection, and maintenance:

    line 146

≈ “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

Dialpad Skill

Send SMS and make voice calls via the Dialpad API.

When to Use

Native tools: submit_draft (registered by this skill's dialpad-draft-callback plugin). Owned entrypoints: list_sms_thread.py, list_sms_inbox.py, list_call_history.py, lookup_contact.py, poll_voicemails.py.

Use this skill to:

  • Send SMS messages (individual or batch)
  • Make voice calls (with TTS or custom voices)
  • Manage contacts and organization settings
  • Inspect SMS and call history through operator tooling when needed

Available Phone Numbers

NumberPurposeFormat
(415) 555-0140Sales TeamDefault for sales context
(415) 555-0100Work/PersonalDefault for work context
(415) 555-0141Support SMS OnlySMS only (no voice)

Quick Start

Send SMS (explicit sender recommended):

bin/send_sms.py --to "+14155551234" --from "+14155550140" --message 'Hello from OpenClaw!'

Send an operator-approved SMS and close a shown approval draft (agent direct path):

bin/send_sms.py --to "+14155551234" --from "+14155550140" --message 'Exact approved text' --resolve-draft-id smsdraft_abc123 --approval-actor-id "telegram-user-123" --approval-actor-username "operator" --json

Create/approve an SMS draft (human approval path):

bin/create_sms_draft.py --thread-key "manual:thread" --to "+14155551234" --from "+14155550140" --message 'Exact draft text' --json
bin/approve_sms_draft.py smsdraft_abc123 --actor-id "telegram-user-123" --actor-username "operator" --approval-token "$DIALPAD_SMS_APPROVAL_TOKEN" --json

Group Intro (mirrored fallback):

bin/send_group_intro.py --prospect "+14155550111" --reference "+14155559999" --confirm-share --from "+14155550100"

Make Call (TTS):

bin/make_call.py --to "+14155551234" --text "This is a call from the agent."

List Recent Calls:

bin/list_calls.py --today --limit 20
bin/list_calls.py --hours 6 --missed --json

Get Call Transcript:

bin/get_call_transcript.py --call-id "call_123" --json
bin/get_call_transcript.py --last --with "+14155551234" --json

Check SMS Thread History:

bin/list_sms_thread.py --phone "+14155551234" --json
bin/list_sms_inbox.py --limit 20 --json

When DIALPAD_LOG_URL is configured, SMS thread/inbox reads and call-list reads use the authenticated theshop interaction log by default. Use bin/list_calls.py --live only for an explicit live Dialpad query and --local for the legacy local calls database. After a successful bin/send_sms.py, the exact outbound observation is recorded through the shared log or queued in the record-only DIALPAD_LOG_OUTBOX; replaying that outbox never sends SMS or places calls. It drains itself on the next send or read, bounded so a dead log never delays the command that triggered it; see references/sms-storage.md for the drain knobs, the run record, and the breach alert.

Sync Direct Dialpad SMS Sends:

bin/sync_sms_export.py --start-date 2026-05-13 --end-date 2026-05-13 --json

Create Contact:

bin/create_contact.py --first-name "Jane" --last-name "Doe" --phone "+14155550123" --email "jane@example.com"

Update Contact:

bin/update_contact.py --id "contact_123" --phone "+14155550123" --job-title "VP"

Key Rules

  1. Destination policy: Outbound SMS and calls support NANP (+1) destinations only, including all NANP territories. Non-NANP international destinations are rejected before any Dialpad API request. Always use E.164 format (e.g., +14155551234); SMS country inference normalizes ten-digit NANP input to explicit +1 E.164. The generated message schedules.send_now command is disabled because stored recipients cannot be validated locally; schedule updates must repeat phone recipients through --data as a JSON array; meeting updates must set call_out through --data as a JSON boolean; and generated meeting create/update commands cannot enable call-out.
  2. Escaping: Use single quotes for inline --message values containing $ to prevent shell expansion (e.g., 'Price is $10').
  3. Safer message input: Prefer --message-file or --message-stdin for pricing text, multi-line copy, or anything shell-sensitive.
  4. Supported agent interface: use bin/*.py wrappers for normal work. They are the stable command contract for agents.
  5. Operator-only surfaces: generated/dialpad and scripts/* are for manual troubleshooting, storage inspection, or operational maintenance, not normal agent task execution.
  6. Auth canonical source: DIALPAD_API_KEY is canonical. DIALPAD_TOKEN is only needed for manual generated CLI troubleshooting (bridge command in Setup below).
  7. SMS sender safety: --from and --profile work|sales are supported. Prefer explicit --from for deterministic routing.
    • --profile maps to configured env vars:
      • work: DIALPAD_PROFILE_WORK_FROM
      • sales: DIALPAD_PROFILE_SALES_FROM
    • default fallback order: DIALPAD_DEFAULT_FROM_NUMBER, then DIALPAD_DEFAULT_PROFILE
    • --allow-profile-mismatch permits explicit/profile mismatches when intentional
    • --dry-run prints sender resolution and the exact message/request preview without an API call
  8. Group intro: bin/send_group_intro.py mirrors intro messages as two one-to-one SMS sends (mirrored_fallback) because true group threads are unsupported via this wrapper.
  9. Call history: bin/list_calls.py is the supported call-history command for agents (supporting --today, --hours, --missed, and --local for offline store queries). bin/list_call_history.py provides dedicated read-only local store queries with --phone, --direction, --min-duration, and --transcript-only filters. Use --json when downstream automation needs a deterministic response envelope.
  10. Call transcripts: bin/get_call_transcript.py is the supported transcript retrieval command for agents. It supports --local to retrieve transcripts deterministically from the local SQLite store without hitting the live Dialpad API. When hitting the live API, it uses Dialpad's plural transcript endpoints (GET /api/v2/transcripts/{call_id} and optional review URL GET /api/v2/transcripts/{call_id}/url); singular /api/v2/transcript/{id} is not the supported endpoint. It is transcript-only; AI recap, CRM enrichment, and follow-up drafting are intentionally separate work.
  11. Call & SMS local stores: Incoming webhooks persist events in local append-only SQLite databases (DIALPAD_SMS_DB defaulting to /home/art/niemand/logs/sms.db, and DIALPAD_CALLS_DB defaulting to /home/art/niemand/logs/calls.db). Before saying a contact was not messaged or called, inspect local history first. For SMS, run bin/list_sms_thread.py --phone PHONE --json and check has_outbound / outbound_count. For calls, run bin/list_call_history.py --phone PHONE --json.
  12. SMS export sync: bin/sync_sms_export.py imports Dialpad Stats text export metadata into local SQLite for direct Dialpad UI sends and other out-of-band messages. It skips existing message IDs to preserve webhook-captured text.
  13. Create/Update Contact Behavior: bin/create_contact.py upserts shared/local contacts by phone/email match (or forces create with --allow-duplicate). bin/update_contact.py updates by --id with partial fields.
  14. Current-turn verification: "Already sent" and "Already updated" are only valid after a fresh current-turn tool result, not from stale session memory. If the current turn has not verified the action yet, say that plainly and run the tool now.
  15. SMS receipt ledger: Successful sends through bin/send_sms.py, both legs of bin/send_group_intro.py, and approval-lane sends append authoritative JSONL receipt evidence to DIALPAD_SMS_RECEIPT_LEDGER, defaulting to /data/.openclaw/state/dialpad/sms-receipts.jsonl. Failed sends and dry runs do not write receipts.
  16. Receipt phrasing: When confirming an SMS send to an operator, always include To: <number> in the receipt text so the downstream guard can bind the claim to the ledger recipient.
  17. Delivery-status webhooks: bin/create_sms_webhook.py create provisions the SMS subscription with status: true. Authenticated sparse delivery events update only the matching sms.db row by provider message ID and never enter inbound dedupe, OpenClaw hooks, Telegram, or approval-draft fan-out. Receipt events require a parseable provider event_timestamp; an empty or accepted result is required alongside delivered, while unknown, stale, and contradictory states fail closed.
  18. Identity guardrail: For first-contact work, soft signals like first name, area code, industry, or job title are not enough to merge or update a contact. Keep uncertain identity draft-only for autonomous sends and contact mutation — let the CRM layer prove the match before auto-sending or mutating anything. However, explicit operator approval (rule 20) lifts the draft-only send restriction: the whole point of the approval path is that the human makes the identity judgment call. Do not refuse to send a draft that an operator has explicitly approved in the current turn.
  19. Inbound context guardrail: Webhook inboundContext briefs explain identity evidence, recent SMS/call continuity, source status, and draft basis. Low-confidence Sales SMS and missed calls may get generic approval drafts, but not personalized claims. Known contacts only get context-aware, CRM-aware, meeting-aware, or availability-aware approval drafts when identity confidence is high and relevant continuity is no older than 14 days; stale or degraded context stays brief-only. Calendar lookup is only for obvious meeting logistics and demo-prospect availability requests, not every inbound SMS. If availability cannot be verified, do not turn the CRM generic fallback into a scheduling reply. Demo-context missed calls may include operator-only prior-comms provenance from SMS/Gmail counts; raw comms bodies must not be copied into customer-facing draft text. If a model is used for final wording, it must receive compact tool-call facts plus the deterministic fallback and fail closed to that fallback on unsafe or unsupported output.
  20. Inbound automation guardrail: Dialpad inbound hooks may create SMS approval drafts, but they must not send customer SMS directly. Use bin/approve_sms_draft.py only with a real human actor id and an operator-only DIALPAD_SMS_APPROVAL_TOKEN; agent/bot actors are rejected by the trusted approval ledger.
  21. Operator-approved agent sends: Agents may send SMS with bin/send_sms.py after explicit current-turn operator approval. When resolving a shown approval draft, pass --resolve-draft-id plus --approval-actor-id/--approval-actor-username so the draft closes as an audited agent_direct_send; that actor context is agent-asserted unless it came through a trusted Telegram/shell approval surface. Risky drafts still require the existing two-step approval state before --confirm-risk can send.
  22. Opt-out guardrail: Explicit opt-out language is a hard stop. Do not create override drafts or send follow-ups on those threads unless a human operator handles the conversation outside automation.
  23. Operator notification ownership: One inbound SMS or missed call should create one operator-visible Telegram message per target by default. When the local Dialpad Telegram approval card and OpenClaw hook route point at the same Telegram destination, the local card owns visibility and the hook is context-only with deliver=false. Use DIALPAD_ALLOW_DUPLICATE_OPERATOR_DELIVERY=1 only for intentional same-target fanout.
  24. Generated CLI command structure & safe discovery: The underlying generated CLI uses <resource> <resource>.<method> naming (e.g. dialpad sms sms.send, dialpad users users.get, dialpad call call.create_call). Avoid probing guessed subcommands such as users list (which does not exist in this generated CLI). When inspecting commands, use generated/dialpad <resource> --help or rely on bin/*.py wrappers.
  25. Send SMS managed environment & direct API fallback: bin/send_sms.py executes generated Dialpad CLI calls through a managed environment built into the skill copy: the wrapper's own interpreter runs generated/dialpad with vendor/ (constructed from the pinned, hash-verified requirements.txt by scripts/build_vendor.py; provenance and delivery in docs/reference/vendor-build.md and docs/reference/runtime-copies.md) on PYTHONPATH, so click always imports from the repo tree — never from an ambient system python, a PATH lookup, or a uv binary that non-interactive runtimes (systemd, agent exec, cron) do not reliably carry (the #89 uv-discovery fix regressed in #155 exactly this way). If the generated CLI or its Python runtime dependencies are missing anyway, bin/send_sms.py falls back to direct Dialpad API execution as a safety net, and a draft claimed for such a local failure returns to a retryable pending/risk_pending state instead of being terminally failed, so approved outbound SMS messages are neither lost nor stuck on environment gaps.

Reference Documentation

  • references/api-reference.md — Wrapper behavior, operator CLI reference, Webhooks
  • references/openclaw-integration.md — End-to-end setup guide for wiring this repo's webhook server to OpenClaw with human approval defaults
  • references/sms-storage.md — SQLite commands, FTS5 search, legacy storage
  • references/voice-options.md — List of available TTS voices (Budget & Premium)
  • references/architecture.md — System architecture, wrappers, and CLI generation
  • THEORY.MD — Design rationale: shell-corruption invariant, 401 classification, inbound-policy-once model

Operational Tools

Use these only for manual operator workflows, storage inspection, and maintenance:

python3 scripts/sms_sqlite.py list
python3 scripts/webhook_server.py

Setup

Required environment variable:

export DIALPAD_API_KEY="your_key"

Operator auth bridge for manual generated CLI troubleshooting:

export DIALPAD_TOKEN="${DIALPAD_TOKEN:-$DIALPAD_API_KEY}"

Optional:

export ELEVENLABS_API_KEY="your_key"
export DIALPAD_USER_MAP='{"+14155550100": "5765607478525952"}'
export DIALPAD_PROFILE_WORK_FROM="+14155550100"
export DIALPAD_PROFILE_SALES_FROM="+14155550140"
export DIALPAD_DEFAULT_PROFILE="work"
export DIALPAD_DEFAULT_FROM_NUMBER="+14155550140"
export DIALPAD_SMS_RECEIPT_LEDGER="/data/.openclaw/state/dialpad/sms-receipts.jsonl"
export DIALPAD_DRAFT_CALLBACK_URL="http://host.docker.internal:8888/internal/draft-callback"

For merged agent-draft flow, keep DIALPAD_DRAFT_CALLBACK_URL aligned with the OpenClaw dialpad-draft-callback plugin callbackUrl config. The default host.docker.internal URL is for gateway-container to host-webhook delivery.

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.