@delegance/sdk 0.1.0 → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Delegance Inc.
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,308 +1,392 @@
1
1
  # @delegance/sdk
2
2
 
3
- TypeScript SDK for integrating with the Delegance Brokerage platform.
3
+ Get commercial insurance quotes from your code. This TypeScript SDK covers the Delegance API: signup, instant quotes, bind, payments, policies, certificates, bonds, claims, and documents.
4
4
 
5
- ## Installation
5
+ Full docs: https://portal.delegancebrokerage.com/docs/sdk
6
+
7
+ ## Install
6
8
 
7
9
  ```bash
8
10
  npm install @delegance/sdk
9
- # or
10
- yarn add @delegance/sdk
11
- # or
12
- pnpm add @delegance/sdk
13
11
  ```
14
12
 
15
- ## Quick Start
13
+ Requires Node 18 or newer. The package ships CommonJS, ESM, and TypeScript types.
14
+
15
+ Prefer a terminal? [`@delegance/cli`](https://www.npmjs.com/package/@delegance/cli) runs the same flows: `npx -y @delegance/cli quote start --demo`.
16
+
17
+ ## Availability
18
+
19
+ - Quoting is open to every account. A new account from `createSignup()` can quote as soon as the owner confirms the email.
20
+ - Bind, payments, bonds, and claims are live over the API.
21
+ - A licensed Delegance broker approves every bind before coverage starts. `instantQuotes.bind()` files the request; it never charges a card.
22
+ - A switched-off feature answers 503 with `reason: 'not_available'`. The SDK does not retry that answer.
23
+
24
+ ## One key, one company
25
+
26
+ An API key belongs to exactly one company. Every call acts on that company, so you never pass a company id to quote.
27
+
28
+ - `documents.upload()` and `documents.list()` take an optional `companyId`. Leave it out. If you name a company the key does not belong to, the server answers 403 with `code: 'company_mismatch'`.
29
+ - A customer key cannot create companies. `companies.create()` and `companies.upsert()` answer 403 `tenant_key_cannot_create`. Open a new account with `createSignup()` instead.
30
+ - `companies.list()` returns the key's own company.
31
+
32
+ ## Quick start: sign up and quote
33
+
34
+ ```typescript
35
+ import { createSignup, newDeviceSecret, DeleganceClient } from '@delegance/sdk';
36
+
37
+ // 1. Create the account. Keep `device.secret` on this machine only.
38
+ const signup = createSignup();
39
+ const account = await signup.createAccount({
40
+ name: 'Acme Bakery',
41
+ contactEmail: 'owner@acmebakery.com',
42
+ industry: 'commercial bakery',
43
+ companySize: '2-10',
44
+ address: { street: '100 Main St', city: 'Austin', state: 'TX', zip: '78701' },
45
+ });
46
+ const device = newDeviceSecret();
47
+ const { enrollmentId } = await signup.enroll(account.bootstrapKey, {
48
+ deviceSecretHash: device.hash,
49
+ firstName: 'Dana',
50
+ lastName: 'Owner',
51
+ });
52
+ await signup.requestVerification(account.bootstrapKey);
53
+
54
+ // 2. The owner clicks the emailed confirm link. Then collect the API key.
55
+ const result = await signup.waitForKey(enrollmentId, device.secret);
56
+ if (result.status !== 'ready') throw new Error('Confirm link not clicked yet');
57
+
58
+ // 3. Quote. See "Instant quotes" below for the answers.
59
+ const client = new DeleganceClient({ apiKey: result.apiKey });
60
+ const session = await client.instantQuotes.start(['general_liability']);
61
+ ```
62
+
63
+ Store `result.apiKey` somewhere safe. The server shows it once.
64
+
65
+ ## Quick start: existing API key
16
66
 
17
67
  ```typescript
18
68
  import { DeleganceClient } from '@delegance/sdk';
19
69
  import fs from 'fs';
20
70
 
21
- // Initialize the client
22
- const client = new DeleganceClient({
23
- apiKey: 'dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
24
- });
25
-
26
- // Create a company
27
- const company = await client.companies.create({
28
- name: 'ABC Construction',
29
- industry: 'construction',
30
- companySize: '11-25',
31
- contactEmail: 'contact@abcconstruction.com',
32
- fein: '12-3456789',
33
- address: {
34
- street: '123 Main St',
35
- city: 'Austin',
36
- state: 'TX',
37
- zip: '78701'
38
- }
39
- });
71
+ const client = new DeleganceClient({ apiKey: process.env.DELEGANCE_API_KEY! });
40
72
 
41
- console.log('Created company:', company.id);
73
+ // The key's own company
74
+ const [company] = await client.companies.list();
75
+ console.log(company.name);
42
76
 
43
- // Upload a policy document
77
+ // Upload a policy. No companyId: the key decides the company.
44
78
  const doc = await client.documents.upload({
45
- companyId: company.id,
46
79
  file: fs.readFileSync('./GL-Policy-2025.pdf'),
47
80
  fileName: 'GL-Policy-2025.pdf',
48
- documentType: 'policy'
81
+ documentType: 'policy',
49
82
  });
50
83
 
51
- console.log('Document uploaded:', doc.id, 'Status:', doc.status);
52
-
53
- // Wait for extraction to complete
54
- const result = await client.documents.waitForExtraction(doc.id, {
55
- timeoutMs: 60000,
56
- onStatusChange: (status) => console.log('Processing:', status)
57
- });
84
+ const done = await client.documents.waitForExtraction(doc.id, { timeoutMs: 120_000 });
85
+ console.log(done.extraction?.policies);
58
86
 
59
- if (result.extraction) {
60
- console.log('Extracted policies:', result.extraction.policies);
61
- }
87
+ // Read what is in force
88
+ const { policies } = await client.policies.list();
62
89
  ```
63
90
 
64
- ## Configuration
91
+ ## Instant quotes
92
+
93
+ Real carrier premiums over the API. The flow has four calls.
65
94
 
66
- ### Client Options
95
+ 1. `start(coverages)` returns the session and the carriers' questions.
96
+ 2. `submit()` files your answers with the carriers.
97
+ 3. `status()` or `waitForQuotes()` returns premiums as carriers answer.
98
+ 4. `bind()` requests a bind on one offer.
99
+
100
+ Coverage ids: `general_liability`, `workers_comp`, `business_owners`, `cyber`, `professional_liability`, `tech_eo`, `commercial_auto`, `commercial_property`, `umbrella`.
67
101
 
68
102
  ```typescript
69
- const client = new DeleganceClient({
70
- // Required: Your API key
71
- apiKey: 'dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
72
-
73
- // Optional: API base URL (default: https://app.delegance.ai)
74
- baseUrl: 'https://app.delegance.ai',
75
-
76
- // Optional: Request timeout in ms (default: 30000)
77
- timeout: 30000,
78
-
79
- // Optional: Number of retry attempts (default: 3)
80
- retries: 3,
81
-
82
- // Optional: Custom headers
83
- headers: {
84
- 'X-Custom-Header': 'value'
85
- }
103
+ const session = await client.instantQuotes.start(['general_liability', 'workers_comp']);
104
+
105
+ // Each question: { id, label, type, options?, required, prefill?, suggested?, showIf?, requiredIfShown? }
106
+ const answers: Record<string, unknown> = {};
107
+ for (const q of session.questions) {
108
+ if (q.prefill !== undefined && q.prefill !== null) answers[q.id] = q.prefill;
109
+ }
110
+ answers.annual_revenue = 850000;
111
+
112
+ const submitted = await client.instantQuotes.submit({
113
+ submissionId: session.submissionId,
114
+ sessionVersion: session.sessionVersion,
115
+ answers,
86
116
  });
117
+
118
+ if (submitted.status === 'submitted') {
119
+ const status = await client.instantQuotes.waitForQuotes(session.submissionId, { timeoutMs: 10 * 60_000 });
120
+ console.log('Coverage starts', status.effectiveDate);
121
+ for (const q of status.carrierQuotes) {
122
+ console.log(q.coverage, q.carrierName, q.premium, q.limits, q.deductible, q.bindable);
123
+ }
124
+ }
87
125
  ```
88
126
 
89
- ### Environment Variables
127
+ ### Answers
90
128
 
91
- You can also create a client from environment variables:
129
+ - Key every answer by question `id`.
130
+ - `yes_no`: `'yes'` or `'no'`. `number`, `currency`, `percentage`: a number. `select`: one option `value`. Anything else: text.
131
+ - `prefill` comes from the insured's own account data. Send it back unless the insured changes it.
132
+ - `suggested` is a guess from another source. It counts only after the insured confirms it.
133
+ - `showIf: { questionId, equals }` hides a follow-up until the parent answer matches. A hidden question is never required. `requiredIfShown` makes it required while visible.
92
134
 
93
- ```typescript
94
- import { createClientFromEnv } from '@delegance/sdk';
135
+ ### Submit results
95
136
 
96
- // Reads DELEGANCE_API_KEY and optionally DELEGANCE_BASE_URL
97
- const client = createClientFromEnv();
98
- ```
137
+ `submit()` returns `status`:
138
+
139
+ | Status | Meaning |
140
+ |--------|---------|
141
+ | `submitted` | Filed with the instant carriers. Poll `status()` for premiums. |
142
+ | `acord_pending` | Some coverages need a person. A broker task opened (`brokerTaskId`). |
143
+ | `no_carriers` | No carrier can quote this instantly. Hand it to a broker (below). |
144
+
145
+ A second submit for coverage that already has a live request answers 409 `active_request_exists`. To re-quote, call `start(coverages, { fresh: true })` and submit with `replace: true`.
146
+
147
+ ### Reading quotes
148
+
149
+ `status()` returns `carrierQuotes`, one row per carrier offer:
150
+
151
+ - `premium`: annual premium in dollars, or null before the carrier prices it.
152
+ - `bindable`: true when you can bind the offer.
153
+ - `limits`: readable limits, for example `"$1M per occurrence / $2M aggregate"` or `"Statutory / $1M employers liability"`.
154
+ - `deductible`: dollars, 0 for none. Today only cyber quotes carry one; other lines return null.
155
+ - `effectiveDate` (on the status): when coverage would start, as `YYYY-MM-DD`.
99
156
 
100
- ## API Reference
157
+ The three fields are new in 0.2.1. An older server leaves them out, so treat a missing field like null.
101
158
 
102
- ### Companies
159
+ Compare prices within one coverage only. The cheapest bindable offer per coverage is the one to show first.
103
160
 
104
- #### Create a Company
161
+ ### No instant carrier: broker handoff
105
162
 
106
163
  ```typescript
107
- const company = await client.companies.create({
108
- name: 'ABC Construction', // Required
109
- industry: 'construction', // Required
110
- companySize: '11-25', // Required: '1', '2-10', '11-25', '26-50', '51-100', '100-499', '500+'
111
- contactEmail: 'contact@abc.com', // Required
112
-
113
- // Optional fields
114
- legalName: 'ABC Construction LLC',
115
- fein: '12-3456789',
116
- website: 'https://abc.com',
117
- yearEstablished: '2015',
118
- companyStructure: 'LLC',
119
- description: 'Commercial construction company',
120
- contactPhone: '+1-555-123-4567',
121
- externalId: 'ams-12345', // For deduplication
122
- address: {
123
- street: '123 Main St',
124
- suite: 'Suite 100',
125
- city: 'Austin',
126
- state: 'TX',
127
- zip: '78701'
128
- }
129
- });
164
+ if (submitted.status === 'no_carriers') {
165
+ const plan = await client.instantQuotes.brokerHandoffQuestions(session.submissionId, answers);
166
+ const acordAnswers: Record<string, unknown> = {}; // answer plan.questions the same way
167
+ const handoff = await client.instantQuotes.brokerHandoff(session.submissionId, {
168
+ answers: acordAnswers,
169
+ instantAnswers: answers,
170
+ complete: true,
171
+ });
172
+ console.log(handoff.status, handoff.brokerTaskId); // 'broker_review'
173
+ }
130
174
  ```
131
175
 
132
- #### List Companies
176
+ A broker places the risk with a market that quotes by hand and emails the owner. `complete: false` says you stopped early; the broker asks the rest. A repeat call returns the same broker task. `status().brokerReview` shows the task.
133
177
 
134
- ```typescript
135
- // List your company
136
- const companies = await client.companies.list();
178
+ ### Bind and pay
137
179
 
138
- // Include companies created via this API key
139
- const allCompanies = await client.companies.list({ includeCreated: true });
180
+ ```typescript
181
+ const status = await client.instantQuotes.status(session.submissionId);
182
+ const offer = status.carrierQuotes.find((q) => q.bindable && q.premium !== null);
183
+ if (!offer) throw new Error('No bindable offer yet');
184
+
185
+ const bind = await client.instantQuotes.bind(session.submissionId, offer.opportunityId);
186
+ console.log(bind.status, bind.next);
187
+
188
+ // After a broker approves the bind, a payment request appears
189
+ const due = (await client.payments.list()).filter((p) => p.payable);
190
+ if (due[0]) {
191
+ const { checkoutUrl } = await client.payments.checkout(due[0].id);
192
+ console.log('Pay here:', checkoutUrl); // Stripe checkout
193
+ }
140
194
  ```
141
195
 
142
- #### Upsert (Create if not exists)
196
+ A Delegance broker approves each bind request. You get an email when payment is ready. One live bind per coverage per submission: a repeat returns the existing request (`existing: true`).
143
197
 
144
- ```typescript
145
- const { company, isExisting } = await client.companies.upsert({
146
- name: 'ABC Construction',
147
- industry: 'construction',
148
- companySize: '11-25',
149
- contactEmail: 'contact@abc.com',
150
- externalId: 'ams-12345' // Deduplication key
151
- });
198
+ `bindWithStepUp()` covers fast bind, where the owner attests the answers and enters a 6-digit code we email. Fast bind with the carrier's own checkout (tier A) is not open yet; until it is, every bind goes to a broker.
199
+
200
+ ## Device login (existing customers)
152
201
 
153
- if (isExisting) {
154
- console.log('Company already existed');
202
+ A person who already has a portal account approves a new device in the browser. No API key is needed to start.
203
+
204
+ ```typescript
205
+ import { createDeviceLogin, newDeviceSecret, DeleganceClient } from '@delegance/sdk';
206
+
207
+ const login = createDeviceLogin();
208
+ const device = newDeviceSecret();
209
+ const start = await login.start({ deviceSecretHash: device.hash, deviceName: 'build-server' });
210
+ console.log(`Open ${start.verificationUriComplete} and confirm code ${start.userCode}`);
211
+
212
+ const result = await login.waitForKey(start.loginId, device.secret);
213
+ if (result.status === 'ready') {
214
+ const client = new DeleganceClient({ apiKey: result.apiKey });
215
+ const info = await client.auth.keyInfo(); // scopes, company, expiry
216
+ if (info.refreshable) await client.auth.refresh(); // extend a CLI key
155
217
  }
156
218
  ```
157
219
 
158
- ### Documents
220
+ ## API keys
159
221
 
160
- #### Upload a Document
222
+ Create keys in the portal (Settings > API keys), with `delegance signup` / `delegance auth login`, or with `client.apiKeys.create()` from a key with the `admin` scope.
161
223
 
162
- ```typescript
163
- import fs from 'fs';
224
+ Key rules:
164
225
 
165
- const doc = await client.documents.upload({
166
- companyId: 'company-uuid',
167
- file: fs.readFileSync('./policy.pdf'), // Buffer, Blob, or base64 string
168
- fileName: 'policy.pdf',
169
-
170
- // Optional
171
- documentType: 'policy', // 'policy', 'loss_run', 'financial_statement', 'sov', 'application', 'certificate', 'endorsement', 'bor', 'other'
172
- externalId: 'doc-123', // For deduplication
173
- metadata: { source: 'ams' }
226
+ - A key belongs to one company.
227
+ - A new key expires in 90 days unless you pick another expiry.
228
+ - A key with `write`, `admin`, or `claims:write` must expire within 365 days. `expiresInDays: 'never'` is allowed only for a read-only key; otherwise the server answers 400 `write_key_requires_expiry`.
229
+ - `claims:write` can be created only while signed in to the portal (signup and device login keys for owners and admins carry it).
230
+ - Delegance staff accounts cannot create client keys (403).
231
+ - `appetite:read`, `mcp:read`, `mcp:orin`, and `claims:write` are opt-in. `read` and `write` never imply them.
232
+ - Keys from signup and device login refresh with `client.auth.refresh()`.
233
+
234
+ ```typescript
235
+ const created = await client.apiKeys.create({
236
+ name: 'CI uploader',
237
+ scopes: ['read', 'write'],
238
+ expiresInDays: 90,
174
239
  });
240
+ console.log('Save this key now:', created.key);
175
241
  ```
176
242
 
177
- #### Upload Multiple Documents
243
+ Format: `dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`.
244
+
245
+ ## Limits
246
+
247
+ | Limit | Error |
248
+ |-------|-------|
249
+ | Quote starts per key per day | 429 `quote_limit_reached` |
250
+ | Per-key daily AI budget (chat, quote sessions) | 429 `api_key_budget_exhausted` |
251
+ | Delegance-wide daily API AI budget | 429 `api_budget_exhausted`, `Retry-After: 3600` |
252
+ | Requests per minute | 429, see `client.usage.rateLimits()` |
253
+
254
+ The SDK does not retry a 429. Read `err.retryAfter` (seconds, from the body or the `Retry-After` header) and wait before you call again.
255
+
256
+ ## Other modules
257
+
258
+ Every module hangs off the client. Each one has a docs page with examples.
259
+
260
+ | Module | What it does |
261
+ |--------|--------------|
262
+ | `instantQuotes` | Start, submit, status, broker handoff, bind |
263
+ | `payments` | Premium payment requests and checkout links |
264
+ | `bonds` | Surety bond quotes; you buy on the surety's page |
265
+ | `claims` | File and track claims (needs `claims:write`; accept the claims disclaimer once in the portal) |
266
+ | `policies` | Policies in force, expiring soon, cancellation requests |
267
+ | `coi` | Certificates of insurance |
268
+ | `documents` | Upload, extraction status, list |
269
+ | `sov` | Schedules of values |
270
+ | `chat` | Ask about your coverage |
271
+ | `knowledge` | Notecards and facts extracted from a document |
272
+ | `appetite` | Which carriers will write a risk (needs `appetite:read`) |
273
+ | `leads` | Send a prospect business to Delegance |
274
+ | `onboarding` | Account setup status and verification email |
275
+ | `feedback` | Report a bug, an idea, or a question |
276
+ | `usage` | Billing-period usage, daily usage, rate limits |
277
+ | `apiKeys` | Create, list, revoke keys |
278
+ | `auth` | Key info and refresh |
279
+ | `companies` | The key's own company |
280
+ | `quotes` | Legacy partner quote queue. Submission answers 501; use `instantQuotes` |
281
+
282
+ ### Bonds
178
283
 
179
284
  ```typescript
180
- const { documents, errors } = await client.documents.uploadBatch([
181
- { companyId, file: buffer1, fileName: 'policy1.pdf' },
182
- { companyId, file: buffer2, fileName: 'policy2.pdf' },
183
- { companyId, file: buffer3, fileName: 'policy3.pdf' }
184
- ], { concurrency: 3 });
185
-
186
- console.log(`Uploaded ${documents.length} documents`);
187
- if (errors.length > 0) {
188
- console.error('Failed uploads:', errors);
189
- }
285
+ const start = await client.bonds.start({ state: 'TX', bondName: 'notary' });
190
286
  ```
191
287
 
192
- #### Get Document Status
288
+ ### Claims
193
289
 
194
290
  ```typescript
195
- const doc = await client.documents.get('document-uuid');
291
+ const claim = await client.claims.file({ description: 'Water damage in the kitchen', incidentDate: '2026-10-01' });
292
+ console.log(claim.claimId, claim.status, claim.portalUrl);
293
+ ```
196
294
 
197
- console.log('Status:', doc.status);
198
- // 'pending' | 'processing' | 'completed' | 'processed' | 'error' | 'failed'
295
+ A 403 with `reason: 'gate_not_acknowledged'` means the claims disclaimer is not accepted yet. The owner accepts it once in the portal (`err.body.portalUrl`).
199
296
 
200
- if (doc.extraction) {
201
- console.log('Extracted policies:', doc.extraction.policies);
202
- }
297
+ ### Feedback
298
+
299
+ ```typescript
300
+ const { id } = await client.feedback.submit({
301
+ type: 'bug',
302
+ message: 'quote submit hung on the delivery question',
303
+ context: { command: 'quote submit', exitCode: 1, traceId: 'trace-from-the-error' },
304
+ });
203
305
  ```
204
306
 
205
- #### List Documents
307
+ ### Appetite
206
308
 
207
- ```typescript
208
- // List all documents
209
- const { documents, pagination } = await client.documents.list();
309
+ Needs a key with the `appetite:read` scope. A warm cache hit returns the result at once; a cold match returns a job to poll.
210
310
 
211
- // Filter by type and status
212
- const policies = await client.documents.list({
213
- documentType: 'policy',
214
- status: 'completed',
215
- limit: 50,
216
- offset: 0
311
+ ```typescript
312
+ const start = await client.appetite.match({
313
+ state: 'CA',
314
+ industry: 'commercial bakery',
315
+ coverageTypes: ['general_liability', 'workers_comp'],
217
316
  });
218
-
219
- // Paginate
220
- while (pagination.hasMore) {
221
- const next = await client.documents.list({ offset: pagination.offset + 50 });
222
- // ...
223
- }
317
+ const result = start.status === 'succeeded' ? start : await client.appetite.waitForMatch(start.jobId);
224
318
  ```
225
319
 
226
- #### Wait for Extraction
320
+ ### Leads
321
+
322
+ Idempotent on `externalId`. A contact email that already belongs to a company returns that company.
227
323
 
228
324
  ```typescript
229
- const result = await client.documents.waitForExtraction('document-uuid', {
230
- timeoutMs: 120000, // 2 minutes max
231
- pollIntervalMs: 2000, // Check every 2 seconds
232
- onStatusChange: (status) => console.log('Status:', status)
325
+ const { companyId } = await client.leads.create({
326
+ name: 'Riverbend Bakery',
327
+ contactEmail: 'owner@riverbend.example',
328
+ state: 'TX',
329
+ externalId: 'crm-4411',
233
330
  });
234
-
235
- if (result.status === 'completed') {
236
- console.log('Policies found:', result.extraction?.policies.length);
237
- } else if (result.status === 'error') {
238
- console.error('Processing failed:', result.error?.message);
239
- }
240
331
  ```
241
332
 
242
- ## Error Handling
333
+ ## Use it from an AI agent
243
334
 
244
- The SDK throws typed errors for different scenarios:
335
+ Claude Code, Codex, Gemini CLI, and other coding agents can drive the SDK or the CLI. The CLI is often simpler for an agent: `npx -y @delegance/cli quote start --json` prints the questions as JSON, and `--answers answers.json` files them. See the CLI README section "Use it from an AI agent" and https://portal.delegancebrokerage.com/docs/cli.
245
336
 
246
- ```typescript
247
- import {
248
- AuthenticationError,
249
- ValidationError,
250
- NotFoundError,
251
- RateLimitError,
252
- DeleganceError
253
- } from '@delegance/sdk';
337
+ Rules that keep an agent safe:
254
338
 
255
- try {
256
- await client.documents.get('invalid-uuid');
257
- } catch (error) {
258
- if (error instanceof AuthenticationError) {
259
- console.error('Invalid API key');
260
- } else if (error instanceof NotFoundError) {
261
- console.error('Document not found');
262
- } else if (error instanceof ValidationError) {
263
- console.error('Invalid input:', error.details);
264
- } else if (error instanceof RateLimitError) {
265
- console.error('Rate limited, retry after:', error.retryAfter);
266
- } else if (error instanceof DeleganceError) {
267
- console.error('API error:', error.message, 'Status:', error.statusCode);
268
- }
269
- }
339
+ - Read the key from `DELEGANCE_API_KEY`. Never paste it into a prompt or a file the agent commits.
340
+ - Pass your own `idempotencyKey` to `submit()`, `bind()`, and `brokerHandoff()` when the agent may retry across restarts. The same key lands on the same request.
341
+ - Gate on `err.reason` and `err.code`, never on the message text.
342
+
343
+ ## Configuration
344
+
345
+ ```typescript
346
+ const client = new DeleganceClient({
347
+ apiKey: 'dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // required
348
+ baseUrl: 'https://portal.delegancebrokerage.com', // default
349
+ timeout: 30000, // ms, default 30000
350
+ retries: 3, // default 3
351
+ headers: { 'X-Custom-Header': 'value' },
352
+ });
270
353
  ```
271
354
 
272
- ## TypeScript Support
355
+ `createClientFromEnv()` reads `DELEGANCE_API_KEY` and, if set, `DELEGANCE_BASE_URL`.
273
356
 
274
- The SDK is written in TypeScript and includes full type definitions:
357
+ ## Errors
275
358
 
276
359
  ```typescript
277
- import type {
278
- Company,
279
- Document,
280
- DocumentWithExtraction,
281
- ExtractedPolicy,
282
- CreateCompanyInput,
283
- UploadDocumentInput
360
+ import {
361
+ DeleganceError,
362
+ AuthenticationError, // 401
363
+ ValidationError, // 400
364
+ ForbiddenError, // 403
365
+ NotFoundError, // 404
366
+ RateLimitError, // 429
367
+ NotImplementedError, // 501
284
368
  } from '@delegance/sdk';
285
369
 
286
- // Use Zod schemas for validation
287
- import { CreateCompanySchema, DocumentTypeSchema } from '@delegance/sdk';
288
-
289
- const result = CreateCompanySchema.safeParse(data);
290
- if (!result.success) {
291
- console.error(result.error.issues);
370
+ try {
371
+ await client.documents.list({ companyId: 'another-company' });
372
+ } catch (err) {
373
+ if (err instanceof DeleganceError) {
374
+ console.error(err.statusCode, err.reason, err.code, err.message, err.traceId);
375
+ }
292
376
  }
293
377
  ```
294
378
 
295
- ## Getting an API Key
379
+ Every error carries:
296
380
 
297
- 1. Log in to the Delegance dashboard
298
- 2. Navigate to Settings > API Keys
299
- 3. Click "Create API Key"
300
- 4. Copy the key (it's only shown once!)
381
+ - `statusCode`: the HTTP status.
382
+ - `reason`: the machine-readable reason from the body, when sent (`not_available`, `quote_limit_reached`, `active_request_exists`, `attestation_required`, ...).
383
+ - `code`: the body's `code`, when sent (`company_mismatch`). New in 0.2.1; on 0.2.0 read `err.body.code`.
384
+ - `body`: the parsed error body, for fields such as `portalUrl`.
385
+ - `traceId`: quote it to support.
301
386
 
302
- API keys have the format: `dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
387
+ Retries: the SDK retries network failures and 5xx answers other than 501, with backoff. It never retries a 4xx (including 429), a 501, or a 503 with `reason: 'not_available'`.
303
388
 
304
389
  ## Support
305
390
 
306
- - Documentation: https://docs.delegance.ai
307
- - Issues: https://github.com/delegance/delegance-sdk/issues
308
- - Email: support@delegance.ai
391
+ - Docs: https://portal.delegancebrokerage.com/docs/sdk
392
+ - Email: support@delegancebrokerage.com