@delegance/sdk 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -1,27 +1,35 @@
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
13
  Requires Node 18 or newer. The package ships CommonJS, ESM, and TypeScript types.
16
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
17
  ## Availability
18
18
 
19
- - **Quoting over the API is live for enrolled companies.** Delegance turns it on account by account today.
20
- - **Public access opens on 2026-10-06.** From that date a new account created with `createSignup()` can quote.
21
- - **Until an account has access,** the instant-quote, bind, and payment calls throw a `DeleganceError` with `statusCode` 503 and `reason` `'not_available'`. The SDK does not retry that answer.
22
- - **A licensed Delegance broker reviews every bind.** `instantQuotes.bind()` creates a request. It never charges a card and never binds coverage by itself.
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
23
25
 
24
- ## Sign up and quote
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
25
33
 
26
34
  ```typescript
27
35
  import { createSignup, newDeviceSecret, DeleganceClient } from '@delegance/sdk';
@@ -33,6 +41,7 @@ const account = await signup.createAccount({
33
41
  contactEmail: 'owner@acmebakery.com',
34
42
  industry: 'commercial bakery',
35
43
  companySize: '2-10',
44
+ address: { street: '100 Main St', city: 'Austin', state: 'TX', zip: '78701' },
36
45
  });
37
46
  const device = newDeviceSecret();
38
47
  const { enrollmentId } = await signup.enroll(account.bootstrapKey, {
@@ -46,250 +55,258 @@ await signup.requestVerification(account.bootstrapKey);
46
55
  const result = await signup.waitForKey(enrollmentId, device.secret);
47
56
  if (result.status !== 'ready') throw new Error('Confirm link not clicked yet');
48
57
 
49
- // 3. Quote general liability.
58
+ // 3. Quote. See "Instant quotes" below for the answers.
50
59
  const client = new DeleganceClient({ apiKey: result.apiKey });
51
60
  const session = await client.instantQuotes.start(['general_liability']);
52
- // Answer session.questions (keyed by question id), then submit.
53
- const submitted = await client.instantQuotes.submit({
54
- submissionId: session.submissionId,
55
- sessionVersion: session.sessionVersion,
56
- answers: {},
57
- });
58
- const status = await client.instantQuotes.waitForQuotes(session.submissionId);
59
61
  ```
60
62
 
61
- The [`@delegance/cli`](https://www.npmjs.com/package/@delegance/cli) package runs the same flow from a terminal (`delegance signup`, `delegance quote start`).
63
+ Store `result.apiKey` somewhere safe. The server shows it once.
62
64
 
63
- ## Quick Start (existing API key)
65
+ ## Quick start: existing API key
64
66
 
65
67
  ```typescript
66
68
  import { DeleganceClient } from '@delegance/sdk';
67
69
  import fs from 'fs';
68
70
 
69
- // Initialize the client
70
- const client = new DeleganceClient({
71
- apiKey: 'dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
72
- });
71
+ const client = new DeleganceClient({ apiKey: process.env.DELEGANCE_API_KEY! });
73
72
 
74
- // Create a company
75
- const company = await client.companies.create({
76
- name: 'ABC Construction',
77
- industry: 'construction',
78
- companySize: '11-25',
79
- contactEmail: 'contact@abcconstruction.com',
80
- fein: '12-3456789',
81
- address: {
82
- street: '123 Main St',
83
- city: 'Austin',
84
- state: 'TX',
85
- zip: '78701'
86
- }
87
- });
88
-
89
- console.log('Created company:', company.id);
73
+ // The key's own company
74
+ const [company] = await client.companies.list();
75
+ console.log(company.name);
90
76
 
91
- // Upload a policy document
77
+ // Upload a policy. No companyId: the key decides the company.
92
78
  const doc = await client.documents.upload({
93
- companyId: company.id,
94
79
  file: fs.readFileSync('./GL-Policy-2025.pdf'),
95
80
  fileName: 'GL-Policy-2025.pdf',
96
- documentType: 'policy'
81
+ documentType: 'policy',
97
82
  });
98
83
 
99
- console.log('Document uploaded:', doc.id, 'Status:', doc.status);
100
-
101
- // Wait for extraction to complete
102
- const result = await client.documents.waitForExtraction(doc.id, {
103
- timeoutMs: 60000,
104
- onStatusChange: (status) => console.log('Processing:', status)
105
- });
84
+ const done = await client.documents.waitForExtraction(doc.id, { timeoutMs: 120_000 });
85
+ console.log(done.extraction?.policies);
106
86
 
107
- if (result.extraction) {
108
- console.log('Extracted policies:', result.extraction.policies);
109
- }
87
+ // Read what is in force
88
+ const { policies } = await client.policies.list();
110
89
  ```
111
90
 
112
- ## Configuration
91
+ ## Instant quotes
92
+
93
+ Real carrier premiums over the API. The flow has four calls.
94
+
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.
113
99
 
114
- ### Client Options
100
+ Coverage ids: `general_liability`, `workers_comp`, `business_owners`, `cyber`, `professional_liability`, `tech_eo`, `commercial_auto`, `commercial_property`, `umbrella`.
115
101
 
116
102
  ```typescript
117
- const client = new DeleganceClient({
118
- // Required: Your API key
119
- apiKey: 'dlg_xxxxxxxx_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
120
-
121
- // Optional: API base URL (default: https://portal.delegancebrokerage.com)
122
- baseUrl: 'https://portal.delegancebrokerage.com',
123
-
124
- // Optional: Request timeout in ms (default: 30000)
125
- timeout: 30000,
126
-
127
- // Optional: Number of retry attempts (default: 3)
128
- retries: 3,
129
-
130
- // Optional: Custom headers
131
- headers: {
132
- 'X-Custom-Header': 'value'
133
- }
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,
134
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
+ }
135
125
  ```
136
126
 
137
- ### Environment Variables
127
+ ### Answers
138
128
 
139
- 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.
140
134
 
141
- ```typescript
142
- import { createClientFromEnv } from '@delegance/sdk';
135
+ ### Submit results
143
136
 
144
- // Reads DELEGANCE_API_KEY and optionally DELEGANCE_BASE_URL
145
- const client = createClientFromEnv();
146
- ```
137
+ `submit()` returns `status`:
147
138
 
148
- ## API Reference
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). |
149
144
 
150
- ### Companies
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`.
151
146
 
152
- #### Create a Company
147
+ ### Reading quotes
153
148
 
154
- ```typescript
155
- const company = await client.companies.create({
156
- name: 'ABC Construction', // Required
157
- industry: 'construction', // Required
158
- companySize: '11-25', // Required: '1', '2-10', '11-25', '26-50', '51-100', '100-499', '500+'
159
- contactEmail: 'contact@abc.com', // Required
160
-
161
- // Optional fields
162
- legalName: 'ABC Construction LLC',
163
- fein: '12-3456789',
164
- website: 'https://abc.com',
165
- yearEstablished: '2015',
166
- companyStructure: 'LLC',
167
- description: 'Commercial construction company',
168
- contactPhone: '+1-555-123-4567',
169
- externalId: 'ams-12345', // For deduplication
170
- address: {
171
- street: '123 Main St',
172
- suite: 'Suite 100',
173
- city: 'Austin',
174
- state: 'TX',
175
- zip: '78701'
176
- }
177
- });
178
- ```
149
+ `status()` returns `carrierQuotes`, one row per carrier offer:
179
150
 
180
- #### List Companies
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`.
181
156
 
182
- ```typescript
183
- // List your company
184
- const companies = await client.companies.list();
157
+ The three fields are new in 0.2.1. An older server leaves them out, so treat a missing field like null.
185
158
 
186
- // Include companies created via this API key
187
- const allCompanies = await client.companies.list({ includeCreated: true });
188
- ```
159
+ Compare prices within one coverage only. The cheapest bindable offer per coverage is the one to show first.
189
160
 
190
- #### Upsert (Create if not exists)
161
+ ### No instant carrier: broker handoff
191
162
 
192
163
  ```typescript
193
- const { company, isExisting } = await client.companies.upsert({
194
- name: 'ABC Construction',
195
- industry: 'construction',
196
- companySize: '11-25',
197
- contactEmail: 'contact@abc.com',
198
- externalId: 'ams-12345' // Deduplication key
199
- });
200
-
201
- if (isExisting) {
202
- console.log('Company already existed');
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'
203
173
  }
204
174
  ```
205
175
 
206
- ### Documents
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.
207
177
 
208
- #### Upload a Document
178
+ ### Bind and pay
209
179
 
210
180
  ```typescript
211
- import fs from 'fs';
212
-
213
- const doc = await client.documents.upload({
214
- companyId: 'company-uuid',
215
- file: fs.readFileSync('./policy.pdf'), // Buffer, Blob, or base64 string
216
- fileName: 'policy.pdf',
217
-
218
- // Optional
219
- documentType: 'policy', // 'policy', 'loss_run', 'financial_statement', 'sov', 'application', 'certificate', 'endorsement', 'bor', 'other'
220
- externalId: 'doc-123', // For deduplication
221
- metadata: { source: 'ams' }
222
- });
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
+ }
223
194
  ```
224
195
 
225
- #### Upload Multiple Documents
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`).
197
+
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)
201
+
202
+ A person who already has a portal account approves a new device in the browser. No API key is needed to start.
226
203
 
227
204
  ```typescript
228
- const { documents, errors } = await client.documents.uploadBatch([
229
- { companyId, file: buffer1, fileName: 'policy1.pdf' },
230
- { companyId, file: buffer2, fileName: 'policy2.pdf' },
231
- { companyId, file: buffer3, fileName: 'policy3.pdf' }
232
- ], { concurrency: 3 });
233
-
234
- console.log(`Uploaded ${documents.length} documents`);
235
- if (errors.length > 0) {
236
- console.error('Failed uploads:', errors);
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
237
217
  }
238
218
  ```
239
219
 
240
- #### Get Document Status
220
+ ## API keys
241
221
 
242
- ```typescript
243
- const doc = await client.documents.get('document-uuid');
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.
244
223
 
245
- console.log('Status:', doc.status);
246
- // 'pending' | 'processing' | 'completed' | 'processed' | 'error' | 'failed'
224
+ Key rules:
247
225
 
248
- if (doc.extraction) {
249
- console.log('Extracted policies:', doc.extraction.policies);
250
- }
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,
239
+ });
240
+ console.log('Save this key now:', created.key);
251
241
  ```
252
242
 
253
- #### List 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
254
283
 
255
284
  ```typescript
256
- // List all documents
257
- const { documents, pagination } = await client.documents.list();
285
+ const start = await client.bonds.start({ state: 'TX', bondName: 'notary' });
286
+ ```
258
287
 
259
- // Filter by type and status
260
- const policies = await client.documents.list({
261
- documentType: 'policy',
262
- status: 'completed',
263
- limit: 50,
264
- offset: 0
265
- });
288
+ ### Claims
266
289
 
267
- // Paginate
268
- while (pagination.hasMore) {
269
- const next = await client.documents.list({ offset: pagination.offset + 50 });
270
- // ...
271
- }
290
+ ```typescript
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);
272
293
  ```
273
294
 
274
- #### Wait for Extraction
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`).
296
+
297
+ ### Feedback
275
298
 
276
299
  ```typescript
277
- const result = await client.documents.waitForExtraction('document-uuid', {
278
- timeoutMs: 120000, // 2 minutes max
279
- pollIntervalMs: 2000, // Check every 2 seconds
280
- onStatusChange: (status) => console.log('Status:', status)
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' },
281
304
  });
282
-
283
- if (result.status === 'completed') {
284
- console.log('Policies found:', result.extraction?.policies.length);
285
- } else if (result.status === 'error') {
286
- console.error('Processing failed:', result.error?.message);
287
- }
288
305
  ```
289
306
 
290
307
  ### Appetite
291
308
 
292
- Which carriers will write a risk. Needs an API key with the `appetite:read` scope, which `read` and `write` do not include. A warm cache hit returns the result at once; a cold match returns a job to poll.
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.
293
310
 
294
311
  ```typescript
295
312
  const start = await client.appetite.match({
@@ -297,19 +314,12 @@ const start = await client.appetite.match({
297
314
  industry: 'commercial bakery',
298
315
  coverageTypes: ['general_liability', 'workers_comp'],
299
316
  });
300
-
301
- const result = start.status === 'succeeded'
302
- ? start
303
- : await client.appetite.waitForMatch(start.jobId);
304
-
305
- for (const c of result.carrierResults) {
306
- console.log(c.carrier, c.overallStatus, c.productsAvailable);
307
- }
317
+ const result = start.status === 'succeeded' ? start : await client.appetite.waitForMatch(start.jobId);
308
318
  ```
309
319
 
310
320
  ### Leads
311
321
 
312
- Send a prospect business to Delegance. Idempotent on `externalId`, and a contact email that already belongs to a company returns that company.
322
+ Idempotent on `externalId`. A contact email that already belongs to a company returns that company.
313
323
 
314
324
  ```typescript
315
325
  const { companyId } = await client.leads.create({
@@ -320,77 +330,63 @@ const { companyId } = await client.leads.create({
320
330
  });
321
331
  ```
322
332
 
323
- ## Error Handling
333
+ ## Use it from an AI agent
324
334
 
325
- 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.
326
336
 
327
- ```typescript
328
- import {
329
- AuthenticationError,
330
- ForbiddenError,
331
- ValidationError,
332
- NotFoundError,
333
- NotImplementedError,
334
- RateLimitError,
335
- DeleganceError
336
- } from '@delegance/sdk';
337
+ Rules that keep an agent safe:
337
338
 
338
- try {
339
- await client.documents.get('invalid-uuid');
340
- } catch (error) {
341
- if (error instanceof AuthenticationError) {
342
- console.error('Invalid API key');
343
- } else if (error instanceof ForbiddenError) {
344
- console.error('Key lacks the scope for this call');
345
- } else if (error instanceof NotFoundError) {
346
- console.error('Document not found');
347
- } else if (error instanceof NotImplementedError) {
348
- console.error('Not available over the API yet:', error.message);
349
- } else if (error instanceof ValidationError) {
350
- console.error('Invalid input:', error.details);
351
- } else if (error instanceof RateLimitError) {
352
- console.error('Rate limited, retry after:', error.retryAfter);
353
- } else if (error instanceof DeleganceError) {
354
- console.error('API error:', error.message, 'Status:', error.statusCode);
355
- }
356
- }
357
- ```
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
358
344
 
359
- The SDK retries network failures and 5xx responses other than 501. It never retries a 4xx or a 501.
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
+ });
353
+ ```
360
354
 
361
- ## TypeScript Support
355
+ `createClientFromEnv()` reads `DELEGANCE_API_KEY` and, if set, `DELEGANCE_BASE_URL`.
362
356
 
363
- The SDK is written in TypeScript and includes full type definitions:
357
+ ## Errors
364
358
 
365
359
  ```typescript
366
- import type {
367
- Company,
368
- Document,
369
- DocumentWithExtraction,
370
- ExtractedPolicy,
371
- CreateCompanyInput,
372
- UploadDocumentInput
360
+ import {
361
+ DeleganceError,
362
+ AuthenticationError, // 401
363
+ ValidationError, // 400
364
+ ForbiddenError, // 403
365
+ NotFoundError, // 404
366
+ RateLimitError, // 429
367
+ NotImplementedError, // 501
373
368
  } from '@delegance/sdk';
374
369
 
375
- // Use Zod schemas for validation
376
- import { CreateCompanySchema, DocumentTypeSchema } from '@delegance/sdk';
377
-
378
- const result = CreateCompanySchema.safeParse(data);
379
- if (!result.success) {
380
- 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
+ }
381
376
  }
382
377
  ```
383
378
 
384
- ## Getting an API Key
379
+ Every error carries:
385
380
 
386
- 1. Log in to the portal at https://portal.delegancebrokerage.com
387
- 2. Open Settings > API keys
388
- 3. Click "Create API Key"
389
- 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.
390
386
 
391
- 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'`.
392
388
 
393
389
  ## Support
394
390
 
395
- - Documentation: https://portal.delegancebrokerage.com/docs/sdk
391
+ - Docs: https://portal.delegancebrokerage.com/docs/sdk
396
392
  - Email: support@delegancebrokerage.com