Fax Send

SkillDocs & knowledge

Send outbound faxes through a guarded provider adapter and route inbound fax-to-email attachments into downstream document workflows.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Fax Send skill

What this skill tells your AI

The instructions your AI receives, as published by hybridaione/hybridclaw in skills/fax-send/SKILL.md and read by ahel’s review.

Use this skill when a user asks to fax a supported file, web page, text note, or PDF to an external fax number, or when an inbound fax arrives as an email PDF and needs routing to a document workflow such as DATEV Belegtransfer, file storage, or client records.

Response Contract

  • Return exactly one user-facing summary per fax request. Do not restate the same plan, missing-input list, or no-send notice in a second format.
  • For provider results, write the status sentence once. Do not emit both a markdown-styled version and a plain-text duplicate of the same sentence.
  • Do not use decorative emoji, checkmarks, sign-off text, or "ready to proceed" filler in fax responses.
  • If setup is blocked by missing secrets, report each missing secret exactly once and only if it is actually required: SINCH_FAX_PROJECT_ID plus either SINCH_FAX_BASIC_AUTH or SINCH_FAX_OAUTH_TOKEN. Never claim a Sinch service ID secret is required for normal outbound sending.
  • When the user provides text content such as "Hallo Welt", use the helper's helper-generated PDF upload path after the recipient fax number, stored Sinch project ID, stored credential, and explicit approval are available. Omit from when the user wants to use the Sinch Fax service default sender. Do not ask the user for a service ID for a normal outbound send; Sinch uses the product's default Fax service when serviceId is omitted.
  • A live http_request to Sinch is terminal for that send attempt. After it returns, stop and report the provider result. Do not use web_search, web_fetch, the PDF skill, local PDF scripts, helper source inspection, or ad hoc replacement requests, and do not ask to retry in the same response, unless the user explicitly asks for debugging in a new turn.

Scope

  • outbound fax send from a content URL or direct generated-PDF text upload
  • outbound fax send from a local generated PDF file
  • Sinch Fax API request construction for EU-resident Sinch projects/services
  • delivery status lookup and status-to-audit-event classification
  • structured audit persistence through src/fax/accounting.ts
  • fax-page usage accounting through HybridClaw UsageTotals
  • inbound fax-to-email operating recipe through the existing email channel
  • eval scenarios for successful send, busy/retry, failed delivery, and inbound PDF handoff flows

Direct modem control, T.38/SIP live transmission, binary local file upload, and cover-page templating are outside this skill slice.

Credential Rules

Store credentials in HybridClaw encrypted runtime secrets. Never paste them into chat or helper arguments.

For Sinch Basic auth, set the base64 value of username:password in this order:

  1. Browser admin: open the active HybridClaw admin URL ending in /admin/secrets and set SINCH_FAX_BASIC_AUTH.
  2. Browser /chat or TUI fallback: /secret set SINCH_FAX_BASIC_AUTH "<base64-username-password>".
  3. Local console fallback:
hybridclaw secret set SINCH_FAX_BASIC_AUTH "<base64-username-password>"

For Sinch OAuth, set a bearer token only if the operator has a token minting process, using the same setup order:

  1. Browser admin: open the active HybridClaw admin URL ending in /admin/secrets and set SINCH_FAX_OAUTH_TOKEN.
  2. Browser /chat or TUI fallback: /secret set SINCH_FAX_OAUTH_TOKEN "<access-token>".
  3. Local console fallback:
hybridclaw secret set SINCH_FAX_OAUTH_TOKEN "<access-token>"

The helper emits either secretHeaders: [{ name: "Authorization", secretName: "SINCH_FAX_BASIC_AUTH", prefix: "Basic" }] or bearerSecretName: "SINCH_FAX_OAUTH_TOKEN" so the gateway injects the secret server-side.

Set the Sinch project default once so normal sends do not need provider account arguments:

  1. Browser admin: open the active HybridClaw admin URL ending in /admin/secrets and set SINCH_FAX_PROJECT_ID.
  2. Browser /chat or TUI fallback: /secret set SINCH_FAX_PROJECT_ID "<sinch-project-id>".
  3. Local console fallback:
hybridclaw secret set SINCH_FAX_PROJECT_ID "<sinch-project-id>"

Default Workflow

  1. Confirm the recipient fax number in E.164 format and the content URL, local PDF file, or text upload content. Use stored SINCH_FAX_PROJECT_ID by default. Include --from only when the user explicitly supplies a sender fax number; otherwise omit it so Sinch uses the Fax service default sender. Omit serviceId unless the user explicitly provides a non-default Fax service ID in the same request.
  2. Run plan for natural-language requests when details are incomplete.
  3. Require explicit operator approval before fax.send; faxing is an external document delivery action and can incur per-page cost.
  4. Build the request with fax_send.cjs http-request send. Do not hand-author Sinch API JSON or secret references.
  5. Pass only the emitted httpRequest object to http_request for live sends.
  6. After the http_request returns, stop tool use for that send attempt and summarize the returned status/body. If Sinch returns 4xx/5xx, report it as the provider response; do not search the web, inspect PDF tooling, or retry with a different payload shape in the same turn.
  7. Record/inspect audit intent or call the runtime accounting helper:
    • fax.send.start before dispatch
    • fax.send.delivered when status is COMPLETED
    • fax.send.failed when status is FAILURE
  8. Use classify-status after status polling or webhook payloads to turn provider states into the expected audit event and retry decision.

Command Contract

Run the helper:

node skills/fax-send/fax_send.cjs --help

Plan a request without contacting a provider:

node skills/fax-send/fax_send.cjs --format json plan "Fax the signed contract to +49 89 1234567"

Build a guarded Sinch outbound request:

node skills/fax-send/fax_send.cjs --format json http-request send \
  --provider sinch \
  --auth basic \
  --content-url https://example.com/signed-contract.pdf \
  --to +49891234567 \
  --page-count 3 \
  --label costCenter=legal \
  --operator-grant

Look up delivery status:

node skills/fax-send/fax_send.cjs --format json http-request status \
  --fax-id 01F3J0G1M4WQR6HGY6HCF6JA0K

Classify a provider status into audit and retry handling:

node skills/fax-send/fax_send.cjs --format json classify-status \
  --fax-id 01F3J0G1M4WQR6HGY6HCF6JA0K \
  --provider sinch \
  --status FAILURE \
  --error-type CALL_ERROR \
  --error-message "BUSY"

List provider reference support:

node skills/fax-send/fax_send.cjs --format json providers

List eval scenarios:

node skills/fax-send/fax_send.cjs --format json eval-scenarios

Inbound Fax-To-Email

Inbound fax is handled by the existing email channel. Configure the fax provider or telco portal to forward inbound faxes as PDF attachments to a dedicated mailbox, then configure HybridClaw email polling for that mailbox.

Use a narrow sender allowlist when the provider uses stable sender domains, and route attachment PDFs with normal document skills. See docs/content/channels/fax.md for the operator recipe and reference YAML.

Build a guarded Sinch generated-PDF text upload request:

node skills/fax-send/fax_send.cjs --format json http-request send \
  --provider sinch \
  --auth basic \
  --text "Hallo Welt" \
  --filename hallo-welt.pdf \
  --to +498920931098 \
  --page-count 1 \
  --operator-grant

For generated PDFs, including requests that combine text with an image, first create the PDF with the bundled PDF script and then send that file. Do not write ad hoc PDF or multipart scripts.

node skills/pdf/scripts/create_pdf.mjs fax.pdf \
  --image-url https://github.com/HybridAIOne/hybridclaw/blob/main/docs/hero.png?raw=true \
  --text "Hello World! This Claw can fax!"

node skills/fax-send/fax_send.cjs --format json http-request send \
  --provider sinch \
  --auth basic \
  --file fax.pdf \
  --to +498920931098 \
  --page-count 1 \
  --operator-grant

Working Rules

  • For URL-based sends, use a public HTTP(S) contentUrl for a Sinch-supported file type or web page. Do not send local paths or private intranet URLs to a fax provider.
  • For generated PDF files, use --file path/to/file.pdf so the helper emits a secret-backed multipart/form-data request with the PDF as the file part. Do not write custom multipart builders.
  • For short user-provided text, use --text so the helper emits a secret-backed multipart/form-data request with a generated PDF file upload. Do not invoke skills/pdf, create_pdf.mjs, web_search, or web_fetch to convert or verify this path; the fax helper owns the PDF payload shape.
  • For plan responses, give one concise no-send summary only. Do not repeat the same plan in a second format, do not mirror raw helper JSON after a human summary, and do not add decorative emoji, sign-off text, or readiness filler.
  • Normalize German numbers to E.164 before dispatch, for example +49 89 1234567 becomes +49891234567.
  • Treat SUPERFINE as higher cost/risk than the default FINE; use it only when the user needs small text or detailed scans.
  • Preserve provider fax IDs in all handoffs and audit notes.
  • If a live send returns 401 or 403, stop after the first failure and ask the operator to verify the stored Sinch credential.
  • If delivery fails with a line/busy/call error and retries remain, report the retry plan instead of resending manually in parallel.
  • Cost per fax is page-based. Helper output includes costMeasurement.system = "UsageTotals" plus unit = "fax-page" and the provided pageCount so evals can verify the accounting contract.
  • Runtime integrations should call recordFaxUsageEvent() after provider acceptance or final delivery so UsageTotals.billable_units includes fax-page quantity and provider cost.
  • Runtime integrations should call recordFaxSendStart(), recordFaxSendDelivered(), and recordFaxSendFailed() from src/fax/accounting.ts so the structured audit table contains real fax.send.* rows with provider message IDs.

Signals

GitHub stars
132
Forks
12
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
fax-send
Source
github.com/hybridaione/hybridclaw