# About the Prontuno SDKs

What the official Node.js, PHP, and Laravel SDKs do, which one to choose, and how they handle keys, retries, and pagination.

Prontuno has official SDKs for Node.js, PHP, and Laravel. Each one is a client for the [Prontuno API](https://prontuno.com/api-reference/general/introduction/): it sends email and manages domains, suppressions, webhook endpoints, API keys, and SMTP credentials, and it verifies the webhooks Prontuno sends you.

This page explains how the SDKs work and why they behave as they do. To start using one, follow the guide for your language:

- [Send email with the Node.js SDK](https://prontuno.com/docs/integrations/send-email-with-nodejs/)
- [Send email with the PHP SDK](https://prontuno.com/docs/integrations/send-email-with-php/)
- [Send email from Laravel](https://prontuno.com/docs/integrations/send-email-from-laravel/)

## The three packages

| SDK | Package | Runs on |
| --- | --- | --- |
| Node.js | [`prontuno`](https://www.npmjs.com/package/prontuno) on npm | Node.js 22 or newer, Bun, Deno 2, and Cloudflare Workers with Node.js compatibility |
| PHP | [`prontuno/prontuno-php`](https://packagist.org/packages/prontuno/prontuno-php) on Packagist | PHP 8.3 or newer, any framework |
| Laravel | [`prontuno/prontuno-laravel`](https://packagist.org/packages/prontuno/prontuno-laravel) on Packagist | PHP 8.3 or newer with Laravel 12 or 13 |

The Laravel package is not a wrapper around the PHP SDK. It is built on Laravel’s own HTTP client, so it fits the way a Laravel application is configured and tested. It adds a `prontuno` mail driver, so Mailables and notifications can send through Prontuno without code changes, plus a facade, a config file, and middleware that verifies webhooks.

The source for each SDK is on GitHub: [prontuno-node](https://github.com/prontuno/prontuno-node), [prontuno-php](https://github.com/prontuno/prontuno-php), and [prontuno-laravel](https://github.com/prontuno/prontuno-laravel).

## When to use an SDK

You can send with Prontuno in three ways: an SDK, the REST API directly, or [SMTP](https://prontuno.com/docs/sending/send-email-with-smtp/).

SMTP suits software that already sends through an SMTP server, such as a CMS, a hosted service, or an existing mailer setup. You change a few settings and nothing else.

An SDK suits code you write yourself. Compared with SMTP, the API returns an ID for every email straight away, reports problems as structured errors you can act on, sends batches of up to 1,000 emails in one request, and can manage domains, webhooks, and keys as well as send. Compared with calling the API yourself, an SDK adds retries, pagination, webhook verification, and typed errors, so you don’t have to write them.

For a language without an official SDK, call the REST API with any HTTP client, or use SMTP.

## Sending keys and management keys

Prontuno has more than one [type of API key](https://prontuno.com/docs/sending/api-keys/), and the SDKs use two of them:

- A **Sending** key can only send email, and only from the one verified domain it belongs to.
- A **Management** key can do everything else: read email logs, and manage domains, suppressions, webhooks, API keys, and SMTP credentials. It cannot send.

Splitting the two limits what a leaked key can do. The server that sends receipts needs only a Sending key, which cannot change your DNS records or create new keys. A script that manages webhooks needs only a Management key, which cannot send email from your domains.

Give a client both keys, and it picks the right one for each call: sends use the Sending key and everything else uses the Management key. If a call needs a key the client doesn’t have, the SDK throws a configuration error before making a request, so the mistake shows up in your code rather than as a `403` from the API. You can also give a client a single key, which it uses for every request.

Because a Sending key belongs to one domain, sending from a second domain needs a second key. Each SDK can create a copy of the client that uses another Sending key and keeps every other setting.

## Retries and idempotency

Networks fail, and when a request times out you can’t tell whether Prontuno received it. Retrying a request that reads data is always safe. Retrying a send is not, because it could deliver the same email twice.

The SDKs handle this as follows:

- Requests that read or delete data are retried automatically after a timeout, a network error, a `408` or `429` response, or a `5xx` response. The default is two retries.
- A send is retried only when you pass an [idempotency key](https://prontuno.com/api-reference/general/idempotency/). Prontuno accepts each key once and returns the original response for repeats within 24 hours, so a retried send never delivers twice.
- Requests that create API keys or SMTP credentials, or reset a password, are never retried, so a timeout can’t create a second secret you never see.
- When the API sends a `Retry-After` header, the SDK waits that long, up to 60 seconds.

Pass an idempotency key with every send. Derive it from the event that triggered the email, such as `order-1042-receipt`, so retries made by your own job queue are safe too.

## Field names match the API

Requests and responses use the same snake_case field names as the API, such as `reply_to` and `domain_id`. Anything in the [API reference](https://prontuno.com/api-reference/general/introduction/) applies to the SDK as written, and an API response can be logged or stored without converting it.

## Pagination

List endpoints return one page at a time, with a cursor for the next page. Each SDK has two ways to read a list. `list()` returns a single page, which suits a paginated table in your own interface. `listAll()` in Node.js and PHP, or `lazy()` in Laravel, follows the cursors for you and reads the next page only when you need it, which suits exports and background jobs. Both stop after 1,000 pages by default, and stop with an error if the API returns the same cursor twice, so a bug can’t loop forever.

## Webhook verification

Prontuno signs each webhook request with a secret that only you and Prontuno know. Each SDK checks the signature, rejects requests older than five minutes, and returns a typed event. The signature covers the exact bytes Prontuno sent, so the SDK needs the raw request body, before your framework parses the JSON. See [Verify webhooks with an SDK](https://prontuno.com/docs/integrations/verify-webhooks-with-an-sdk/).

## Next steps

[SDK referenceConfiguration options, methods for every resource, API key routing, errors, and webhook helpers in the Prontuno Node.js, PHP, and Laravel SDKs.](https://prontuno.com/docs/integrations/sdk-reference/)

[API keysChoose a Prontuno API key type, create a key, authenticate API requests, rotate and revoke keys without downtime, and connect MCP clients.](https://prontuno.com/docs/sending/api-keys/)
