Search the docs

Find a page, a section, or an endpoint.

SDKs

The TypeScript and Python clients, generated from one document.

Two clients, TypeScript and Python, both generated from openapi.json — which is itself generated from the schemas the API validates with. An operation has the same name in both, because both take it from the same field.

npm install @pidgeon-ai/sdk

The Python package needs Python 3.11 or later.

Configure once

import { client } from '@pidgeon-ai/sdk';

client.setConfig({ auth: () => process.env.PIDGEON_API_KEY! });

TypeScript has a module-level client the functions use when you do not pass one; Python threads it through each call. That difference is each language's idiom rather than a disagreement about the API.

Call an operation

import { messagesSend } from '@pidgeon-ai/sdk';

const { data, error } = await messagesSend({
body: {
  from: 'you@yourdomain.com',
  to: 'someone@example.com',
  subject: 'First one',
  text: 'It works.',
},
});

messagesSend and messages_send are the same operation. Every one is named this way, so a method you find in one language tells you what to look for in the other.

Errors

TypeScript answers, it does not throw. A call returns { data, error, response }, and error carries the API's own code and message. Pass throwOnError: true on a call that should raise instead.

ts
const { data, error } = await identitiesGet({ path: { id } });

if (error) {
console.error(error.error.code, error.error.message);
}

Python raises. Any failure, a status of 400 or above, raises PidgeonError, whether the contract declares that status or not. A call returns its success model or does not return, so a refused send cannot pass for a sent one. code is the API's own and is what to branch on; message is safe to show a person; retry_after is in seconds when a limit says when to come back.

python
from pidgeon.errors import PidgeonError

try:
  result = identities_get.sync(client=client, id=identity_id)
  print(result.data)
except PidgeonError as error:
  print(error.status_code, error.code, error.message)

Before 1.19.0 a declared failure came back as an Error and an undeclared one as None; a 403 on a send was one of the second kind, and a site read it as sent.

Verifying a webhook

The one thing in the TypeScript package that is written rather than generated, because HMAC is a security contract and not an endpoint:

ts
import { verifyWebhook } from '@pidgeon-ai/sdk';

const event = await verifyWebhook({
rawBody: await request.text(),
headers: request.headers,
secret: process.env.PIDGEON_WEBHOOK_SECRET!,
});

The Python package ships the same thing — pidgeon.webhooks.verify_webhook, keyword arguments, raising WebhookVerificationError rather than returning a boolean nobody checks. Verifying a signature has both, and the three ways to get it wrong by hand.

What message/rfc822 does in Python

GET /messages/{id}/raw returns the original message rather than JSON, and the Python generator will not parse a non-JSON body. The call works: the bytes are on .content, and .parsed is None.