faxforagents 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ ## 0.2.1 (2026-10-29)
4
+
5
+ API version 1.2.3. The generated types document `503 live_provider_unavailable` on the eight operations
6
+ that reach the carrier with a live key (`createFaxQuote`, `createFax`, `cancelFax`, `getNumberInventory`,
7
+ `createNumberOrder`, `createReleaseRequest`, `createPortabilityCheck`, `createPortOrder`). A production
8
+ server with no carrier configured refuses them before any hold or approval is consumed, with `retryable`
9
+ false. No client code changed.
10
+
11
+ ## 0.2.0 (2026-10-28)
12
+
13
+ API version 1.1.0: HTML rendering and image uploads.
14
+
15
+ - `documents.create({ html })` renders HTML on the server, scripts off and no network.
16
+ - `documents.upload` takes a PNG, JPG or TIFF beside a PDF and reads the content type from the
17
+ filename (`guessContentType`, `CONTENT_TYPES`). `createUpload` documents the four content types.
18
+ - New resources, every operation of the document: `numbers` (coverage, inventory, orders, porting,
19
+ sender verification), `extractions` (OCR, fields, classification, summaries, saved schemas),
20
+ `organizations` and `members`. The inbox gains `update`, `delete`, `getDownload` and `download`.
21
+ - `sandbox.createInboundFax` returns `InboxItemSummary`. `InboxItem` carries the number, project,
22
+ media status and read state of a received fax. `Quote` carries `per_page_micro` and `route_label`.
23
+ - A `prepare` script builds the package on a git install.
24
+
25
+ ## 0.1.0 (2026-10-28)
26
+
27
+ First release.
28
+
29
+ - `FaxForAgents` client over `fetch`, every `/v1` operation of API version 0.1.0, ESM and CommonJS.
30
+ - Types generated from the public OpenAPI document with openapi-typescript (`npm run gen:types`).
31
+ - `Idempotency-Key` on every mutating call, retries for 429 and 5xx with `Retry-After`.
32
+ - `iter` helpers on every list, `faxes.wait` and `approvals.wait`.
33
+ - `verifyWebhook` for the Standard Webhooks signature, with rotation support, on Web Crypto.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 JSB Holdings LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,174 @@
1
- # Temporary Holding Version
1
+ # faxforagents
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The TypeScript and JavaScript client for Fax for Agents, <https://faxforagents.com>.
4
+
5
+ Fax for Agents is a fax API, a hosted MCP server and SDKs built so AI agents can send and receive
6
+ faxes. It is made by JSB Holdings LLC.
7
+
8
+ ```bash
9
+ npm install faxforagents
10
+ ```
11
+
12
+ Node 18 or later, Deno, Bun, browsers and edge runtimes. Zero runtime dependencies. The client uses
13
+ `fetch` and Web Crypto. ESM and CommonJS builds, types included.
14
+
15
+ ## Quickstart, against the sandbox
16
+
17
+ A test key (`ffa_test_...`) reaches the sandbox provider only. No carrier, no cost, nothing leaves the
18
+ box. The destination number picks the outcome. Keys are created in the dashboard at
19
+ <https://app.faxforagents.com> or with `POST /v1/api-keys`.
20
+
21
+ ```ts
22
+ import { FaxForAgents } from 'faxforagents';
23
+
24
+ const ffa = new FaxForAgents(); // reads FAXFORAGENTS_API_KEY
25
+
26
+ const doc = await ffa.documents.create({ text: 'Hello from an agent.', filename: 'hello' });
27
+ console.log(doc.id, doc.page_count, doc.sha256.slice(0, 12));
28
+
29
+ const quote = await ffa.quotes.create({ document_id: doc.id, to: '+15550000001' });
30
+ console.log(quote.amount_micro, 'micro, expires', quote.expires_at);
31
+
32
+ let fax = await ffa.faxes.create({ quote_id: quote.id }); // Idempotency-Key minted for you
33
+ console.log(fax.id, fax.state); // queued, never delivered yet
34
+
35
+ fax = await ffa.faxes.wait(fax.id); // polls until a terminal state
36
+ console.log(fax.state, fax.pages_sent, fax.evidence); // delivered, 1, {...}
37
+
38
+ for await (const job of ffa.faxes.iter({ state: 'failed' })) { // follows next_cursor
39
+ console.log(job.id, job.failure_code);
40
+ }
41
+ ```
42
+
43
+ ## What the client does for you
44
+
45
+ - Every mutating call carries an `Idempotency-Key`. When you pass none, a UUID is minted once per call
46
+ and reused on every retry, so a retried send never creates a second fax. Pass
47
+ `{ idempotencyKey }` as the last argument to pick your own.
48
+ - 429 and 5xx answers are retried up to three times. `Retry-After` is honoured. Network errors and
49
+ timeouts are retried the same way.
50
+ - Every list has an `iter` twin that follows `next_cursor`, as an async iterator.
51
+ - `documents.create({ text })`, `{ markdown }` or `{ html }` renders on the server. HTML prints with
52
+ scripts off and no network, inline CSS and `data:` images only, at most 2 MiB.
53
+ - `documents.upload(bytes, { filename })` runs the whole upload path for a PDF or an image (PNG, JPG,
54
+ TIFF): the upload session, the signed PUT with the content type read from the filename, the sha256
55
+ check, the document. An image becomes gray letter pages.
56
+ - `faxes.wait(faxId)` and `approvals.wait(approvalId)` poll with a timeout.
57
+ - `verifyWebhook(secret, headers, body)` checks the Standard Webhooks signature on a delivery.
58
+ - Types are generated from the public OpenAPI document with openapi-typescript. Money is an integer
59
+ count of USD micro-units (`39000` is $0.039). Timestamps are UTC strings.
60
+
61
+ ## Resources
62
+
63
+ Method names are the operationIds of the [OpenAPI document](https://github.com/faxforagents/openapi)
64
+ in camel case, shortened by their resource. The reference at <https://faxforagents.github.io/openapi/>
65
+ names the scope each call needs.
66
+
67
+ | Resource | Calls |
68
+ |---|---|
69
+ | `ffa.identity` | `me`, `listApiKeys`, `createApiKey`, `rotateApiKey`, `revokeApiKey` |
70
+ | `ffa.documents` | `create`, `upload`, `createUpload`, `putUploadContent`, `completeUpload`, `cancelUpload`, `list`, `iter`, `get`, `delete`, `getValidation`, `getDownload`, `download`, `listPages` |
71
+ | `ffa.quotes` | `create`, `get` |
72
+ | `ffa.faxes` | `create`, `list`, `iter`, `get`, `wait`, `listEvents`, `listAttempts`, `getReceipt`, `cancel` |
73
+ | `ffa.webhooks` | `list`, `create`, `update`, `delete`, `rotateSecret`, `test`, `listDeliveries`, `replayDelivery` |
74
+ | `ffa.events` | `list`, `iter` |
75
+ | `ffa.billing` | `balance`, `listLedger`, settings, top-ups, auto top-ups, budgets, `usage`, exports, invoices, subscriptions, `createPortalSession` |
76
+ | `ffa.platform` | `capabilities`, `serviceStatus` |
77
+ | `ffa.sandbox` | `listScenarios`, `createScenario`, `createInboundFax`, `reset` |
78
+ | `ffa.approvals` | `request`, `requestHelp`, `list`, `iter`, `get`, `wait`, `approve`, `reject`, `revoke` |
79
+ | `ffa.policies` | `list`, `create`, `effective`, `get`, `update`, `listVersions`, `evaluate` |
80
+ | `ffa.agents` | `list`, `create`, `get`, `update`, `createKey`, `activity` |
81
+ | `ffa.inbox` | `list`, `iter`, `get`, `update`, `delete`, `getDownload`, `download` |
82
+ | `ffa.numbers` | `coverage`, `inventory`, `createOrder`, `getOrder`, `list`, `get`, `update`, `release`, `checkPortability`, `listPortOrders`, `createPortOrder`, `getPortOrder`, `createPortOutRequest`, `verifySender` |
83
+ | `ffa.extractions` | `create`, `list`, `iter`, `get`, `correct`, `classify`, `listClassifications`, `iterClassifications`, `getClassification`, `summarize`, `listSummaries`, `iterSummaries`, `getSummary`, `listSchemas`, `createSchema`, `getSchema` |
84
+ | `ffa.organizations` | `list`, `current`, `updateCurrent`, `requestDataExport`, `requestDataDeletion` |
85
+ | `ffa.members` | `list`, `update`, `remove`, `listInvitations`, `invite`, `revokeInvitation` |
86
+
87
+ `ffa.request('GET', '/v1/anything')` reaches an operation the resources do not cover yet.
88
+
89
+ ## States
90
+
91
+ A fax job is `queued`, `active`, `partial`, `failed`, `unknown` or `delivered`. `delivered` is written
92
+ only when the carrier's evidence is on the attempt, and `fax.evidence` holds it. `partial` and
93
+ `unknown` never retransmit on their own. Whether to resend is a human decision.
94
+
95
+ Sandbox destinations: `+15550000001` delivered, `+15550000002` busy then delivered, `+15550000003`
96
+ partial, `+15550000004` no answer, `+15550000005` unknown, `+15550000006` provider timeout,
97
+ `+15550000007` invalid number. Any other `+1` number is delivered.
98
+
99
+ ## Errors
100
+
101
+ ```ts
102
+ import { APIError, RateLimitedError } from 'faxforagents';
103
+
104
+ try {
105
+ await ffa.faxes.create({ quote_id: quote.id });
106
+ } catch (err) {
107
+ if (err instanceof RateLimitedError) console.log('retry in', err.retryAfter);
108
+ else if (err instanceof APIError) console.log(err.code, err.message, err.correlationId);
109
+ }
110
+ ```
111
+
112
+ `AuthenticationError` (401), `ScopeError` (403), `NotFoundError` (404), `ConflictError` (409),
113
+ `PaymentRequiredError` (402), `InvalidRequestError` (400, 422), `RateLimitedError` (429) and
114
+ `ServerError` (5xx) all extend `APIError`. `APIConnectionError` and `APITimeoutError` mean the API was
115
+ not reached.
116
+
117
+ ## Approvals
118
+
119
+ A live send needs either a human approval bound to the quote or a delegated policy that allows it.
120
+
121
+ ```ts
122
+ let req = await ffa.approvals.request(quote.id); // a person is mailed
123
+ req = await ffa.approvals.wait(req.id); // pending until they decide
124
+ if (req.status === 'approved') {
125
+ await ffa.faxes.create({ quote_id: quote.id, approval_id: req.id });
126
+ }
127
+ ```
128
+
129
+ An agent's own API key can never approve. `ffa.policies.evaluate({ quote_id })` is a dry run that
130
+ says whether a policy would allow the send.
131
+
132
+ ## Webhooks
133
+
134
+ ```ts
135
+ import { verifyWebhook, parseEvent, SignatureError } from 'faxforagents';
136
+
137
+ // Express: app.post('/hooks/fax', express.raw({ type: '*/*' }), async (req, res) => { ... })
138
+ try {
139
+ const eventId = await verifyWebhook(secret, req.headers, req.body); // raw bytes
140
+ const event = parseEvent(req.body); // event.type: fax.delivered...
141
+ res.sendStatus(200);
142
+ } catch (err) {
143
+ if (err instanceof SignatureError) res.sendStatus(400);
144
+ }
145
+ ```
146
+
147
+ Verify on the raw bytes. `verifyWebhook` is async because it uses Web Crypto. Store the event id to
148
+ drop a redelivery you already handled. During a secret rotation two signatures arrive for 24 hours and
149
+ either one verifies.
150
+
151
+ ## Prices
152
+
153
+ Price card launch-v1 is effective from 2026-10-28. $0.039 per page sent, $0.019 per page received.
154
+ $2 per number per month, $19 per month for the Agent workspace. Sandbox pages cost 0.
155
+ `ffa.platform.capabilities()` returns the card in force.
156
+
157
+ ## Working on this repository
158
+
159
+ ```bash
160
+ npm ci
161
+ npm test # vitest with msw, no network
162
+ FAXFORAGENTS_API_KEY=ffa_test_... npm run test:live # one real sandbox send
163
+ npm run gen:types # src/schema.d.ts from spec/openapi.yaml
164
+ npm run typecheck && npm run build
165
+ sh scripts/truthcheck.sh
166
+ ```
167
+
168
+ `spec/openapi.yaml` is a copy of the public document. To take a new one, copy it over and run
169
+ `npm run gen:types`. `npm install` from a git URL builds the package on install (the `prepare` script),
170
+ so `npm install github:faxforagents/sdk-typescript` works before the package is on npm.
171
+
172
+ ## Licence
173
+
174
+ MIT. See [LICENSE](LICENSE). Security reports go to hello@faxforagents.com, see [SECURITY.md](SECURITY.md).