{"_id":"@a11code/sdk","_rev":"3-96d7d0597ec87587a0e5dda48c66d810","name":"@a11code/sdk","dist-tags":{"latest":"0.2.1"},"versions":{"0.1.6":{"name":"@a11code/sdk","version":"0.1.6","keywords":["a11","sdk","api","cloud","storage","serverless"],"author":{"name":"A11"},"license":"MIT","_id":"@a11code/sdk@0.1.6","maintainers":[{"name":"a11tech","email":"magicforestapp@gmail.com"}],"homepage":"https://github.com/a11-tech/a11-sdk#readme","bugs":{"url":"https://github.com/a11-tech/a11-sdk/issues"},"dist":{"shasum":"fe91ac0471150497b93d903ae6b18ef534b3927a","tarball":"https://registry.npmjs.org/@a11code/sdk/-/sdk-0.1.6.tgz","fileCount":6,"integrity":"sha512-uPYBOhP1bylnLovUFavCuv/K/E3Hnj4/Jo5l1StLjrSu7k7iyYmiTD0ZqW5vQJL7RiceRTHJ5DRouqX6plAeMA==","signatures":[{"sig":"MEQCIEm0L5MRz6himuaWmt5Ef9hG3X1KiK2BPQcaJ7BW+BeZAiBCFjbEYYOE/56OTIVZYKD8Sa1wjnYQNcAG5/i4GSeXrQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":578888},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"bab2f9e83fb7c88bded5a6eac9da5bd9861f5400","scripts":{"dev":"tsup src/index.ts --format cjs,esm --dts --watch","lint":"eslint src/","test":"jest","build":"tsup src/index.ts --format cjs,esm --dts --clean","typecheck":"tsc --noEmit","test:watch":"jest --watch","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"a11tech","email":"magicforestapp@gmail.com"},"repository":{"url":"git+https://github.com/a11-tech/a11-sdk.git","type":"git"},"_npmVersion":"10.9.1","description":"Official A11 SDK for Node.js and TypeScript","directories":{},"_nodeVersion":"23.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f","devDependencies":{"jest":"^29.7.0","nock":"^13.5.0","tsup":"^8.0.0","ts-jest":"^29.1.0","jest-util":"^30.2.0","fast-check":"^4.5.3","typescript":"^5.3.0","@types/jest":"^29.5.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.6_1784927350601_0.8737892643246945","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@a11code/sdk","version":"0.2.0","keywords":["a11","sdk","api","cloud","storage","serverless"],"author":{"name":"A11"},"license":"SEE LICENSE IN LICENSE","_id":"@a11code/sdk@0.2.0","maintainers":[{"name":"a11tech","email":"magicforestapp@gmail.com"}],"homepage":"https://github.com/a11-tech/a11-sdk#readme","bugs":{"url":"https://github.com/a11-tech/a11-sdk/issues"},"dist":{"shasum":"8d7d6c9a03d1a2436138ea0a12dc7572aeda0a8b","tarball":"https://registry.npmjs.org/@a11code/sdk/-/sdk-0.2.0.tgz","fileCount":7,"integrity":"sha512-G9PiyLKa+dYKBBQGBhrM32fHmIY+hddprOwGp+ucVqBVrwxRWemNvP/Fxpf0xMcL0EekFdrlAu4SbG61gio+hA==","signatures":[{"sig":"MEUCIQCe5TnOpfoz2hnnJGF52XB8jrLyiyUNvJaX6hxFbZVivQIgBnMJx6/2vcCotno890zb40CE5mxvESo6qzSmjMv8ekQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":592022},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"gitHead":"35546e1affcecfb6a7f5eb7fbf9736f4e672616e","scripts":{"dev":"tsup src/index.ts --format cjs,esm --dts --watch","lint":"eslint src/","test":"jest","build":"tsup src/index.ts --format cjs,esm --dts --clean","typecheck":"tsc --noEmit","test:watch":"jest --watch","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"_npmUser":{"name":"a11tech","email":"magicforestapp@gmail.com"},"repository":{"url":"git+https://github.com/a11-tech/a11-sdk.git","type":"git"},"_npmVersion":"10.9.1","description":"Official A11 SDK for Node.js and TypeScript","directories":{},"_nodeVersion":"23.3.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"packageManager":"yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f","devDependencies":{"jest":"^29.7.0","nock":"^13.5.0","tsup":"^8.0.0","ts-jest":"^29.1.0","jest-util":"^30.2.0","fast-check":"^4.5.3","typescript":"^5.3.0","@types/jest":"^29.5.0","@types/node":"^20.0.0"},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.2.0_1786131836935_0.30094904528647515","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@a11code/sdk","version":"0.2.1","description":"Official A11 SDK for Node.js and TypeScript","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.mts","default":"./dist/index.mjs"},"require":{"types":"./dist/index.d.ts","default":"./dist/index.js"}}},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean","dev":"tsup src/index.ts --format cjs,esm --dts --watch","test":"jest","test:watch":"jest --watch","lint":"eslint src/","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm run test && npm run build"},"keywords":["a11","sdk","api","cloud","storage","serverless"],"author":{"name":"A11"},"license":"SEE LICENSE IN LICENSE","repository":{"type":"git","url":"git+https://github.com/a11-tech/a11-sdk.git"},"engines":{"node":">=18.0.0"},"devDependencies":{"@types/jest":"^29.5.0","@types/node":"^20.0.0","fast-check":"^4.5.3","jest":"^29.7.0","jest-util":"^30.2.0","nock":"^13.5.0","ts-jest":"^29.1.0","tsup":"^8.0.0","typescript":"^5.3.0"},"packageManager":"yarn@4.10.3+sha512.c38cafb5c7bb273f3926d04e55e1d8c9dfa7d9c3ea1f36a4868fa028b9e5f72298f0b7f401ad5eb921749eb012eb1c3bb74bf7503df3ee43fd600d14a018266f","publishConfig":{"access":"public"},"_id":"@a11code/sdk@0.2.1","gitHead":"2abd39280236e423b9c99ad9ce5f201702238fee","bugs":{"url":"https://github.com/a11-tech/a11-sdk/issues"},"homepage":"https://github.com/a11-tech/a11-sdk#readme","_nodeVersion":"23.3.0","_npmVersion":"10.9.1","dist":{"integrity":"sha512-2tlxtwYG4CugcSLNIulyNThtaB1NnJ0L3AD3NBSa8l/jSPKLKvl2FqBdXHH5Lh/3kEvWWeazqfMlcMCwmKQphw==","shasum":"52ff691456ba843e7cfcc95e8ece71e10e95e4e9","tarball":"https://registry.npmjs.org/@a11code/sdk/-/sdk-0.2.1.tgz","fileCount":7,"unpackedSize":652238,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDzlmy9UgVFVkwVWYUfaU0Uzb+rnbqFvL9MciFOFS9xuwIgLALiAN6XdCBfMszZj0skEzDqnfekxYOsIeAsbq8kFQQ="}]},"_npmUser":{"name":"a11tech","email":"magicforestapp@gmail.com"},"directories":{},"maintainers":[{"name":"a11tech","email":"magicforestapp@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.2.1_1787069870064_0.5850746661840818"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-24T21:09:10.497Z","modified":"2026-08-18T16:17:50.451Z","0.1.6":"2026-07-24T21:09:10.840Z","0.2.0":"2026-08-07T19:43:57.122Z","0.2.1":"2026-08-18T16:17:50.268Z"},"bugs":{"url":"https://github.com/a11-tech/a11-sdk/issues"},"author":{"name":"A11"},"license":"SEE LICENSE IN LICENSE","homepage":"https://github.com/a11-tech/a11-sdk#readme","keywords":["a11","sdk","api","cloud","storage","serverless"],"repository":{"type":"git","url":"git+https://github.com/a11-tech/a11-sdk.git"},"description":"Official A11 SDK for Node.js and TypeScript","maintainers":[{"name":"a11tech","email":"magicforestapp@gmail.com"}],"readme":"# @a11code/sdk\n\nOfficial A11 SDK for Node.js and TypeScript.\n\n## Installation\n\n```bash\nnpm install @a11code/sdk\n# or\nyarn add @a11code/sdk\n```\n\n## Quick Start\n\n```typescript\nimport { A11 } from '@a11code/sdk';\n\n// Initialize with your API key\nconst a11 = new A11({ apiKey: 'A11_your_api_key' });\n\n// Or use environment variables (A11_API_KEY)\nconst a11 = A11.fromEnvironment();\n```\n\n## Services\n\n- [Storage](#storage) - File upload, download, and management\n- [Data](#data-nosql-key-value) - NoSQL key-value storage\n- [Events](#events) - Event publishing and cron triggers\n- [AI](#ai) - Audio, image, text, and LLM capabilities\n- [Location](#location) - Geocoding, routing, and maps\n- [Endpoints](#endpoints) - Serverless function deployment\n- [Auth](#auth) - End-user authentication\n- [Users](#users) - End-user management\n- [Records](#records) - Structured record storage\n- [Catalogs](#catalogs) - Product catalog management\n\n### Picking a data tier — `records` vs `data` vs `storage`\n\nA11 exposes three independent storage surfaces. Pick one per use case; don't reach across.\n\n| If the thing is… | Use | SDK |\n|---|---|---|\n| A row you'll filter / sort / paginate by typed columns | `records` | `a11.records` |\n| An opaque JSON blob keyed by an arbitrary string | `data` | `a11.data` |\n| A file (image, audio, document, anything binary or large) | `storage` | `a11.storage` |\n\n**Mixed shapes:** a row with an attached file lives in both — row in `records`, file in `storage`, URL in the row's `metadata`. Never split a single conceptual thing across `records` + `data`.\n\nConstraints in one line each:\n- `records`: `status` is the only server-side filter — the endpoint reads `status`, `limit` and `offset` and ignores every other query parameter; `count` is the total matching rows across all pages.\n- `data`: 350 KB cap per value; prefix queries only (no `limit`/`offset`); `hasMore` to know if there's overflow — narrow the prefix to \"page\" further.\n- `storage`: prefix queries + `hasMore` (no `limit`/`offset`); visibility toggles between private (presigned URLs) and public (CDN).\n\nNo customer-chosen engine, customer-defined tables/indexes, transactions across calls, or change streams in any tier.\n\n---\n\n## Storage\n\nUpload, download, and manage files.\n\n```typescript\n// Upload a file\nconst file = await a11.storage.upload('images/photo.jpg', imageBuffer, {\n  contentType: 'image/jpeg',\n});\n\n// Download a file\nconst { content, contentType } = await a11.storage.download('images/photo.jpg');\n\n// List files\nconst { data, hasMore } = await a11.storage.list({\n  prefix: 'images/',\n  limit: 100,\n});\n\n// Delete a file\nawait a11.storage.del('images/photo.jpg');\n\n// Get presigned URLs for direct upload/download\nconst uploadUrl = await a11.storage.createUploadUrl('large-file.zip');\nconst downloadUrl = await a11.storage.createDownloadUrl('images/photo.jpg');\n```\n\n---\n\n## Data (NoSQL Key-Value)\n\nStore and retrieve JSON data.\n\n```typescript\n// Store data\nawait a11.data.put('user:123', {\n  name: 'Alice',\n  email: 'alice@example.com',\n  preferences: { theme: 'dark' },\n});\n\n// Retrieve data\nconst user = await a11.data.retrieve('user:123');\nif (user) {\n  console.log(user.value.name); // 'Alice'\n}\n\n// Query by prefix\nconst { data } = await a11.data.query({\n  prefix: 'user:',\n  limit: 10,\n});\n\n// Delete data\nawait a11.data.del('user:123');\n\n// Check existence\nconst exists = await a11.data.exists('user:123');\n```\n\n---\n\n## Events\n\nPublish events and manage scheduled cron triggers.\n\n### Publishing Events\n\n```typescript\n// Publish a single event\nconst result = await a11.events.publish({\n  type: 'user.created',\n  data: { userId: '123', email: 'user@example.com' },\n});\nconsole.log(result.eventIds); // ['evt_abc123']\n\n// Publish multiple events in batch (max 100).\n// A batch can PARTIALLY succeed — compare published against submitted.\nconst batchResult = await a11.events.publishBatch({\n  events: [\n    { type: 'user.created', data: { userId: '1' } },\n    { type: 'user.created', data: { userId: '2' } },\n  ],\n});\nfor (const failure of batchResult.failures) {\n  console.error(`event ${failure.index} rejected: ${failure.message}`);\n}\n\n// List historical events. Cursor-paginated — feed nextCursor back in.\nlet cursor: string | undefined;\ndo {\n  const page = await a11.events.list({ type: 'user.created', limit: 50, cursor });\n  console.log(page.data.map((e) => e.publishedAt));\n  cursor = page.nextCursor;\n} while (cursor);\n```\n\n### Cron Triggers\n\nSchedule recurring events using cron expressions.\n\nEvery `events.crons.*` call requires a paid plan; on a trial account the API\nanswers `402` with error code `TIER_UPGRADE_REQUIRED`.\n\n```typescript\n// Create a cron trigger. `secret` is the HMAC signing key for verifying the\n// webhook deliveries — returned ONCE, here. Store it now.\nconst cron = await a11.events.crons.create({\n  name: 'daily-report',\n  schedule: '0 9 * * *', // 9 AM daily\n  eventType: 'report.generate',\n  webhookUrl: 'https://example.com/webhooks/report',\n  timezone: 'America/New_York',\n  payloadType: 'static',\n  payloadStatic: { reportType: 'daily' },\n});\nconsole.log(cron.status); // 'active' — a cron is always created running\nprocess.env.A11_WEBHOOK_SECRET_DAILY_REPORT = cron.secret;\n\n// List cron triggers, optionally by state\nconst { data, count } = await a11.events.crons.list({ status: 'active' });\n\n// Get a specific cron trigger\nconst trigger = await a11.events.crons.retrieve('cron_123');\n\n// Update a cron trigger\nawait a11.events.crons.update('cron_123', {\n  schedule: '0 10 * * *', // Change to 10 AM\n});\n\n// Pause/resume a cron trigger — both return it with the new `status`.\n// A 'completed' one-time schedule cannot be resumed (400).\nawait a11.events.crons.pause('cron_123');   // status → 'paused'\nawait a11.events.crons.resume('cron_123');  // status → 'active'\n\n// Manually trigger a cron job\nawait a11.events.crons.trigger('cron_123');\n\n// Get cron execution logs — this is where a failed webhook explains itself\nconst { data: logs } = await a11.events.crons.logs('cron_123', { limit: 20 });\nfor (const log of logs.filter((l) => l.status === 'failed')) {\n  console.error(log.triggeredAt, log.responseCode, log.errorMessage);\n}\n\n// Delete a cron trigger\nawait a11.events.crons.del('cron_123');\n```\n\n---\n\n## AI\n\nAI capabilities for audio, image, text, and LLM operations.\n\n### Audio\n\n```typescript\n// Speech to text (transcription).\n// Transcription is ASYNCHRONOUS: transcribe() starts a job and returns a\n// jobName. The transcript arrives on getTranscription(), not on this response.\nconst job = await a11.ai.audio.transcribe({\n  audio: 'https://example.com/audio.mp3', // HTTPS URL or s3://bucket/{tenant-id}/key\n  language: 'en-US',\n});\n\nlet status = await a11.ai.audio.getTranscription(job.jobName);\nwhile (status.status !== 'COMPLETED' && status.status !== 'FAILED') {\n  await new Promise((resolve) => setTimeout(resolve, 2000));\n  status = await a11.ai.audio.getTranscription(job.jobName);\n}\nif (status.status === 'FAILED') {\n  throw new Error(status.failureReason ?? 'Transcription failed');\n}\nconsole.log(status.transcript);\n\n// Transcribe and translate audio.\n// Also ASYNCHRONOUS, and the audio must be a URL — inline base64 is rejected.\n// The translated text arrives on getTranslation(), in `transcript`.\nconst translation = await a11.ai.audio.translate({\n  audio: 'https://example.com/audio.mp3', // HTTPS URL or a storage URI from your upload-url presign\n  targetLanguage: 'es',\n});\n\nlet translated = await a11.ai.audio.getTranslation(translation.jobName);\nwhile (translated.status !== 'COMPLETED' && translated.status !== 'FAILED') {\n  await new Promise((resolve) => setTimeout(resolve, 2000));\n  translated = await a11.ai.audio.getTranslation(translation.jobName);\n}\nif (translated.status === 'FAILED') {\n  throw new Error(translated.failureReason ?? 'Translation failed');\n}\nconsole.log(translated.transcript);\n```\n\n### Image\n\n```typescript\n// Analyze an image (labels, faces, text, moderation)\nconst analysis = await a11.ai.image.analyze({\n  image: base64Image,\n  features: ['labels', 'faces', 'text', 'moderation'],\n});\nconsole.log(analysis.labels);\nconsole.log(analysis.faces);\n\n// Compare two images for face similarity\nconst comparison = await a11.ai.image.compare({\n  sourceImage: base64Image1,\n  targetImage: base64Image2,\n  similarityThreshold: 0.9,\n});\nconsole.log(comparison.matched, comparison.similarity);\n\n// Extract text from an image (OCR)\nconst extracted = await a11.ai.image.extract({\n  image: base64Image,\n});\nconsole.log(extracted.text);\n```\n\n### Text\n\n```typescript\n// Analyze text (sentiment, entities, key phrases)\nconst analysis = await a11.ai.text.analyze({\n  text: \"I love this product! It's amazing.\",\n  language: 'en',\n});\nconsole.log(analysis.sentiment.sentiment); // 'POSITIVE'\nconsole.log(analysis.entities);\nconsole.log(analysis.keyPhrases);\n\n// Extract text from documents (PDF or images)\nconst extracted = await a11.ai.text.extract({\n  document: base64Document,\n  type: 'pdf',\n});\nconsole.log(extracted.text);\n\n// Translate text\nconst translation = await a11.ai.text.translate({\n  text: 'Hello, world!',\n  targetLanguage: 'es',\n});\nconsole.log(translation.translatedText); // 'Hola, mundo!'\n```\n\n### LLM Messages\n\n```typescript\n// Generate text using an AI model\nconst result = await a11.ai.messages.prompt({\n  prompt: 'Explain quantum computing in simple terms',\n  systemPrompt: 'You are a helpful science teacher',\n  maxTokens: 500,\n  temperature: 0.7,\n});\nconsole.log(result.text);\nconsole.log(result.usage.inputTokens, result.usage.outputTokens);\n```\n\n---\n\n## Location\n\nGeocoding, routing, and map tile services.\n\n### Geocoding\n\n```typescript\n// Convert address to coordinates\nconst geocoded = await a11.location.geocode({\n  address: '1600 Amphitheatre Parkway, Mountain View, CA',\n  maxResults: 5,\n});\nfor (const result of geocoded.results) {\n  console.log(result.label, result.latitude, result.longitude);\n}\n\n// Convert coordinates to address\nconst reversed = await a11.location.reverseGeocode({\n  latitude: 37.4224764,\n  longitude: -122.0842499,\n});\nconsole.log(reversed.results[0].label);\nconsole.log(reversed.results[0].addressComponents);\n```\n\n### Routing\n\n```typescript\n// Calculate a route between two points\nconst route = await a11.location.route({\n  origin: { latitude: 37.7749, longitude: -122.4194 },\n  destination: { latitude: 37.3382, longitude: -121.8863 },\n  travelMode: 'Car',\n  avoidTolls: true,\n});\nconsole.log(`Distance: ${route.distance}m`);\nconsole.log(`Duration: ${route.duration}s`);\n\n// With waypoints\nconst routeWithStops = await a11.location.route({\n  origin: { latitude: 37.7749, longitude: -122.4194 },\n  destination: { latitude: 37.3382, longitude: -121.8863 },\n  waypoints: [{ latitude: 37.5585, longitude: -122.2711 }],\n  travelMode: 'Car',\n});\n```\n\n### Map Tiles\n\n```typescript\n// Get map tiles for rendering\nconst tile = await a11.location.mapTiles({\n  z: 12,\n  x: 1234,\n  y: 2345,\n  style: 'standard',\n});\n// tile.tile contains base64-encoded PNG\n```\n\n---\n\n## Endpoints\n\nDeploy and manage serverless functions.\n\n```typescript\n// Deploy a new endpoint\nconst result = await a11.endpoints.deploy({\n  name: 'my-api',\n  description: 'My API endpoint',\n  code: `\n    export default async function handler(req, res) {\n      return res.json({ message: 'Hello!' });\n    }\n  `,\n  runtime: 'nodejs20.x',\n  memory: 128,\n  timeout: 30,\n  environment: {\n    API_KEY: 'secret',\n  },\n});\nconsole.log(result.endpoint.url);\nconsole.log(result.status); // 'deployed' | 'pending' | 'failed'\n\n// List all endpoints\nconst { data, hasMore } = await a11.endpoints.list();\nfor (const endpoint of data) {\n  console.log(endpoint.name, endpoint.url, endpoint.active);\n}\n\n// Get an endpoint\nconst endpoint = await a11.endpoints.retrieve('ept_123');\n\n// Update an endpoint\nconst updated = await a11.endpoints.update('ept_123', {\n  code: newCode,\n  memory: 256,\n});\n\n// Pause/resume an endpoint\nawait a11.endpoints.pause('ept_123');\nawait a11.endpoints.resume('ept_123');\n\n// Delete an endpoint\nawait a11.endpoints.del('ept_123');\n```\n\n---\n\n## Records\n\nSQL-shaped rows on Aurora Postgres. Each record has typed columns (`recordType`, `groupName`, `category`, `status`, `accessLevel`, `lifecycleState`), a `metadata` string array for tags, and a sibling per-record profile store for arbitrary JSON.\n\n```typescript\n// Create a record\nconst record = await a11.records.create({\n  recordName: 'Acme Corp',\n  recordType: 'customer',\n  status: 'active',\n  metadata: ['owner:usr_123', 'tier:enterprise'],\n});\n\n// Retrieve / update / delete\nconst found = await a11.records.retrieve(record.recordId);\nawait a11.records.update(record.recordId, { category: 'premium' });\nawait a11.records.del(record.recordId);\n\n// List + paginate (limit/offset)\nconst { records, count } = await a11.records.list({\n  status: 'active',\n  limit: 50,\n  offset: 0,\n});\n```\n\n**`records.list` filters** — **`status` only**, plus `limit` / `offset`. That is the whole server-side filter surface: the endpoint reads those three query parameters and nothing else, so any other parameter is silently ignored and you get an unfiltered page with a `200`. Filter on anything else — metadata tags, profile contents — client-side after listing. **Do not** pass a `where: { … }` object, and do not expect `?category=…` to work over raw HTTP; neither exists.\n\n`count` on the response is the **total** number of matching rows across all pages, not the length of `records` — use it to decide whether another page is worth fetching.\n\n### Per-record profiles\n\n```typescript\n// Hang arbitrary JSON or a primitive off a record\nawait a11.records.createProfile(record.recordId, 'preferences', {\n  profileBlob: { theme: 'dark', timezone: 'America/New_York' },\n});\n\nconst prefs = await a11.records.retrieveProfile(record.recordId, 'preferences');\n```\n\n---\n\n## Error Handling\n\n```typescript\nimport {\n  A11,\n  AuthenticationError,\n  ValidationError,\n  NotFoundError,\n  RateLimitError,\n} from '@a11code/sdk';\n\ntry {\n  await a11.storage.download('missing.txt');\n} catch (err) {\n  if (err instanceof NotFoundError) {\n    console.log('File not found:', err.hint);\n  } else if (err instanceof AuthenticationError) {\n    console.log('Invalid API key');\n  } else if (err instanceof ValidationError) {\n    console.log('Validation failed:', err.field, err.message);\n  } else if (err instanceof RateLimitError) {\n    console.log('Rate limited, retry after:', err.hint);\n  }\n}\n```\n\n---\n\n## Configuration\n\n```typescript\nconst a11 = new A11({\n  apiKey: 'A11_your_api_key',\n  baseUrl: 'https://api.a11.tech/v1', // default — REST API base\n  streamingUrl: 'https://streaming.a11.tech', // default — SSE/streaming endpoint\n  timeout: 30000, // 30 seconds (default)\n  maxRetries: 3, // retry on 429, 5xx errors (default)\n});\n```\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `apiKey` | `string` | — | Your A11 API key. **Omit it in browser code** — see [Hosted mode](#hosted-mode). Required everywhere else. |\n| `baseUrl` | `string` | `https://api.a11.tech/v1` | REST API base URL. Override for proxied or self-hosted setups. |\n| `streamingUrl` | `string` | `https://streaming.a11.tech` | Streaming/SSE API base URL. Override when using a proxy for streaming. |\n| `timeout` | `number` | `30000` | Request timeout in milliseconds. |\n| `maxRetries` | `number` | `3` | Max retry attempts for 429 and 5xx errors. |\n| `debug` | `boolean` | `false` | Enable debug logging for HTTP requests. |\n| `hosted` | `boolean` | auto | Force [hosted mode](#hosted-mode) on or off, overriding auto-detection. |\n\nBoth `baseUrl` and `streamingUrl` must be valid `http:` or `https:` URLs (or same-origin paths like `/api/v1`). Trailing slashes are stripped automatically.\n\n---\n\n## Hosted mode\n\n**In a browser, construct the client with no API key.**\n\n```typescript\nconst a11 = new A11();\n```\n\nThat selects *hosted mode*: the SDK sends same-origin relative requests\n(`/api/v1`, `/streaming`) and no `Authorization` header, because something in\nfront of your app attaches the credential server-side. Your browser bundle\nnever holds a key.\n\nThe rule is **a browser with no API key is hosted**, and it holds in every\nenvironment an A11 app runs in:\n\n| Where | What attaches the key |\n|---|---|\n| `localhost:5173` during development | your dev-server proxy (the A11 templates ship one) |\n| your A11 build URL | the A11 edge |\n| your own domain | the A11 edge |\n\nDetection is by capability rather than by hostname, deliberately: your launch\ndomain is your own, and no list of hostnames the SDK could ship would know\nabout it.\n\n**Passing a key in a browser is supported but discouraged.** The client then\ncalls the API directly, sends the `Authorization` header, and logs a one-time\nwarning — because a key in a client bundle is readable by anyone who loads your\nsite.\n\n**Server-side (Node, workers, CI) always needs a key.** A relative URL has\nnothing to resolve against there, so hosted mode is never auto-detected and a\nmissing key throws immediately.\n\nIf your proxy is mounted somewhere other than `/api/v1`, pass `baseUrl`\nexplicitly — it wins over the hosted default.\n\n---\n\n## Local Development\n\nWhen developing locally, you can proxy API requests through your dev server to avoid CORS issues and keep your API key out of browser code.\n\n### Vite\n\n```js\n// vite.config.js\nexport default defineConfig({\n  server: {\n    proxy: {\n      '/api/v1': {\n        target: 'https://api.a11.tech',\n        changeOrigin: true,\n        rewrite: (path) => path.replace(/^\\/api\\/v1/, '/v1'),\n      },\n      '/streaming': {\n        target: 'https://streaming.a11.tech',\n        changeOrigin: true,\n        rewrite: (path) => path.replace(/^\\/streaming/, ''),\n      },\n    },\n  },\n});\n```\n\nThen initialize the SDK with relative URLs:\n\n```typescript\nconst a11 = new A11({\n  apiKey: import.meta.env.VITE_A11_API_KEY,\n  baseUrl: 'http://localhost:5173/api/v1',\n  streamingUrl: 'http://localhost:5173/streaming',\n});\n```\n\n### webpack (CRA / custom)\n\n```js\n// webpack.config.js or setupProxy.js\nmodule.exports = function (app) {\n  const { createProxyMiddleware } = require('http-proxy-middleware');\n  app.use('/api/v1', createProxyMiddleware({\n    target: 'https://api.a11.tech',\n    changeOrigin: true,\n    pathRewrite: { '^/api/v1': '/v1' },\n  }));\n  app.use('/streaming', createProxyMiddleware({\n    target: 'https://streaming.a11.tech',\n    changeOrigin: true,\n    pathRewrite: { '^/streaming': '' },\n  }));\n};\n```\n\n### Without a proxy\n\nIf you don't use a dev server proxy, you can point directly at the A11 APIs. The REST API (`api.a11.tech`) allows all origins. For streaming, localhost origins are also allowed:\n\n```typescript\nconst a11 = new A11({\n  apiKey: process.env.A11_API_KEY,\n  // Defaults work for localhost development:\n  // baseUrl: 'https://api.a11.tech/v1',\n  // streamingUrl: 'https://streaming.a11.tech',\n});\n```\n\n---\n\n## Troubleshooting\n\n### CORS errors in the browser\n\n**Symptom:** `Access to fetch at 'https://streaming.a11.tech/...' has been blocked by CORS policy`\n\n**Why it happens:** The streaming API (`streaming.a11.tech`) uses an explicit origin allowlist. If your app is served from a custom domain (not `localhost` or `*.a11.tech`), the browser blocks the response.\n\n**How to fix:**\n\n1. **Use a dev server proxy** (recommended) — see the [Local Development](#local-development) section above. Route `/streaming` through your dev server so the browser makes same-origin requests.\n\n2. **Override `streamingUrl`** — if you have your own backend proxy that forwards to `streaming.a11.tech`, point the SDK at it:\n   ```typescript\n   const a11 = new A11({\n     apiKey: 'A11_...',\n     streamingUrl: 'https://your-proxy.example.com/streaming',\n   });\n   ```\n\n3. **Deploy on A11 hosting** — construct the client with no API key and the A11 edge handles both CORS (requests are same-origin) and key injection. See [Hosted mode](#hosted-mode).\n\nThe REST API (`api.a11.tech`) allows all origins and should not produce CORS errors.\n\n---\n\n## License\n\nProprietary — Copyright (c) 2026 A11. All rights reserved.\nLicensed, not sold; see [LICENSE](LICENSE).\n","readmeFilename":"README.md"}