@cognizy/sdk
v1.1.0
Published
Official TypeScript SDK for the Cognizy Public API.
Readme
@cognizy/sdk
Official TypeScript SDK for the Cognizy Public API.
Available on Professional plans and above.
Install
npm install @cognizy/sdkRequires Node 18+ (for global fetch) or any modern browser.
Quick start
import { Cognizy } from '@cognizy/sdk';
const cognizy = new Cognizy({ apiKey: process.env.COGNIZY_API_KEY! });
// 1. Create a conversation tied to a contact in your system.
const conversation = await cognizy.conversations.create({
contact: { externalId: 'user-42', name: 'Alice', email: '[email protected]' },
});
// 2. Send a message and get the AI reply (synchronous).
const { reply } = await cognizy.messages.send({
conversationId: conversation.id,
message: 'How do I reset my password?',
});
console.log(reply?.body);Multi-tenant SaaS (B2B2C)
If your product embeds Cognizy and serves multiple end customers
(e.g. a property platform whose realtors each have their own customers, a
multi-tenant CRM, a marketplace), pass the optional account field so
Cognizy groups conversations by your customer:
const conversation = await cognizy.conversations.create({
contact: {
externalId: 'user-42',
name: 'Alice',
email: '[email protected]',
},
account: {
externalId: 'acme-456', // your customer / tenant id
name: 'Acme Inc.', // shown as a badge in the inbox
source: 'my-saas', // identifies your product (optional)
metadata: { plan: 'pro', mrr: 499 }, // free-form data
},
});Each unique (source, externalId) becomes an ExternalAccount in
Cognizy — created on the first call, reused on every subsequent call. All
contacts and conversations from the same customer are linked under that
account, with:
- a badge on every conversation in the inbox
- a filter to view conversations by account
- a card on the contact detail showing the account's name, source and metadata
The account field is fully optional and backward-compatible — calls
without it keep working exactly as before.
Streaming
for await (const event of cognizy.messages.stream({
conversationId: conversation.id,
message: 'Tell me a joke',
})) {
if (event.type === 'token') process.stdout.write(event.token);
if (event.type === 'done') console.log('\n— full text:', event.fullText);
if (event.type === 'error') console.error(event.code, event.message);
}Webhook verification
Always verify the signature before trusting a webhook payload.
import { verifyWebhookSignature } from '@cognizy/sdk';
app.post('/webhooks/cognizy', express.raw({ type: 'application/json' }), (req, res) => {
const ok = verifyWebhookSignature({
secret: process.env.COGNIZY_WEBHOOK_SECRET!,
body: req.body, // raw Buffer — do not pre-parse
header: req.headers['x-cognizy-signature'] as string,
});
if (!ok) return res.status(400).send('invalid signature');
const event = JSON.parse(req.body.toString('utf8'));
// … handle event
res.status(200).send('ok');
});Send APIs — transactional email & WhatsApp
Direct sends that don't create an inbox conversation, so receipts, password resets and shipping notices never land in your agents' queue. Delivery is asynchronous: the call resolves once the message is accepted, and the outcome arrives via the status endpoint or a webhook.
Requires the email:send / whatsapp:send scope on the key.
Email from a template
Templates are built in the Cognizy dashboard and referenced by id. {{variables}}
are resolved per recipient:
await cognizy.email.send({
to: '[email protected]',
templateId: 'tpl_123',
variables: { 'contact.firstName': 'Maria', 'order.id': '#1001' },
idempotencyKey: 'order-1001-shipped', // safe to retry
});Don't know which variables a template expects? Ask:
const { items } = await cognizy.email.templates();
// [{ id: 'tpl_123', name: 'Order shipped', variables: ['contact.firstName', 'order.id'], … }]Email with your own body
Skip templateId and pass the content directly:
await cognizy.email.send({
to: '[email protected]',
subject: 'Your order {{order.id}} is on the way',
html: '<p>Hi {{contact.firstName}}, it just shipped.</p>',
variables: { 'contact.firstName': 'Maria', 'order.id': '#1001' },
replyTo: '[email protected]',
});Batches
Up to 1000 recipients per call, each with its own variables. Per-recipient values win over the shared ones:
const res = await cognizy.email.send({
templateId: 'tpl_123',
variables: { 'company.name': 'Acme' }, // applies to everyone
recipients: [
{ to: '[email protected]', variables: { 'contact.firstName': 'Ana' } },
{ to: '[email protected]', variables: { 'contact.firstName': 'Bruno' } },
],
});
// { count: 2, items: [{ id: 'oe_1', … }, { id: 'oe_2', … }] }A batch is all-or-nothing against your quota: if the monthly allowance plus
wallet balance can't cover every recipient, nothing is sent and
EmailQuotaExhaustedError tells you the shortfall. Partial delivery would leave
you unable to tell which recipients still need the message.
import { EmailQuotaExhaustedError } from '@cognizy/sdk';
try {
await cognizy.email.send({ recipients, templateId });
} catch (err) {
if (err instanceof EmailQuotaExhaustedError) {
// { requested: 500, includedRemaining: 120, payableFromWallet: 0, shortfall: 380, … }
await topUpWallet(err.details.shortfall! * (err.details.priceCents ?? 0));
}
}Delivery status
const email = await cognizy.email.get('oe_1');
// status: QUEUED → SENT → DELIVERED → OPENED → CLICKED, or BOUNCED / FAILEDStatus only ever moves forward, so a late provider event can't undo a later one.
Rather than polling, subscribe to the email.message.status webhook.
Same shape. Free-form text and media need an open 24h session window with the recipient; outside it WhatsApp only accepts a template approved in your WABA:
await cognizy.whatsapp.send({
to: '+5511999999999',
type: 'TEMPLATE',
template: { name: 'order_update', language: 'pt_BR', variables: ['Maria', '#1001'] },
});
const msg = await cognizy.whatsapp.get('om_1');
// status: QUEUED → SENT → DELIVERED → READ, or FAILEDNote that Meta bills WhatsApp messages directly to the card on your WhatsApp Business account — Cognizy doesn't charge per message. Platform email is different: it leaves through Cognizy's infrastructure, so it draws on your plan's monthly allowance and then your wallet. Bringing your own SendGrid or Mailgun account exempts you from both.
Errors
The SDK throws typed errors for the cases you'll want to handle:
import {
AuthError,
RateLimitError,
QuotaExceededError,
EmailQuotaExhaustedError,
CognizyError,
} from '@cognizy/sdk';
try {
await cognizy.messages.send({ conversationId, message: 'hi' });
} catch (err) {
if (err instanceof RateLimitError) {
await sleep(err.retryAfterSeconds ?? 60);
} else if (err instanceof QuotaExceededError) {
notifyOps(`Hit ${err.used}/${err.limit} requests this month`);
} else if (err instanceof EmailQuotaExhaustedError) {
notifyOps(`Out of email credit — ${err.details.shortfall} couldn't be sent`);
} else if (err instanceof AuthError) {
rotateKey();
} else if (err instanceof CognizyError) {
console.error(err.status, err.code, err.message);
}
}The two quota errors mean different things and take different fixes:
| Error | Status | Meaning | Fix |
|---|---|---|---|
| QuotaExceededError | 429 | Too many API requests this month | Wait, or raise the plan's request limit |
| EmailQuotaExhaustedError | 400 | Out of emails — allowance and wallet both spent | Add wallet credit, or upgrade the plan |
Waiting never clears the second one; only money does.
API reference
The full Public API is documented in Swagger UI at:
https://<your-host>/api/v1/public/docs(or /api/v1/public/openapi.json for the raw OpenAPI spec).
The SDK currently wraps the most-used surface — agents, conversations, messages (incl. SSE streaming) and the Send APIs for email and WhatsApp — plus webhook signature verification. The following resources are also available in the Public API but are not yet covered by this SDK; call them via fetch (or any HTTP client) with the same Authorization: Bearer <api-key> header:
| Resource | Base path | Notes |
|---|---|---|
| Contacts | /api/v1/public/contacts | List + tag management |
| Knowledge Base | /api/v1/public/knowledge-base | CRUD + semantic search (RAG) |
| Tasks & Boards | /api/v1/public/task-boards, /tasks/:id | Full kanban surface |
| Campaigns | /api/v1/public/campaigns | List + per-campaign analytics |
| Scheduling | /api/v1/public/scheduling | Booking pages + appointments |
| Analytics | /api/v1/public/analytics | Aggregated tenant metrics |
| Webhooks (management) | /api/v1/public/webhooks | Register endpoints, replay deliveries |
Example — direct fetch to a non-SDK endpoint:
const res = await fetch('https://<your-host>/api/v1/public/tasks/' + taskId, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${process.env.COGNIZY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({ title: 'Updated title' }),
});SDK coverage will expand over time — open an issue if a specific resource is blocking you.
Custom base URL
Useful for self-hosted deployments or staging environments.
const cognizy = new Cognizy({
apiKey: '…',
baseUrl: 'https://staging.api.cognizy.ai',
});License
MIT
