", // optional
},
Content: paubox.MessageContent{
PlainText: paubox.Ptr("Plain text body."), // at least one required
HTML: paubox.Ptr("HTML body.
"),
},
Attachments: []paubox.Attachment{
{
FileName: "report.pdf",
ContentType: "application/pdf",
Content: base64EncodedBytes, // base64-encoded file content
},
},
AllowNonTLS: false, // default false: enforce TLS
ForceSecureNotification: paubox.Ptr(true), // optional
}
```
The `paubox.Ptr[T]` helper creates a pointer to any value, which is required for optional fields typed as `*T`:
```go theme={null}
paubox.Ptr("some string") // returns *string
paubox.Ptr(true) // returns *bool
```
### Response
```go theme={null}
type SendMessageResponse struct {
SourceTrackingID string
Data SendMessageData
}
```
## Send a batch
Send up to 50 messages in a single API call. Each message gets its own tracking ID.
```go theme={null}
resp, err := client.SendBatch(ctx, &paubox.SendBatchRequest{
Messages: []paubox.Message{
{
Recipients: []string{"alice@example.com"},
Headers: paubox.MessageHeaders{From: "f@yourdomain.com", Subject: "Hi Alice"},
Content: paubox.MessageContent{PlainText: paubox.Ptr("Hello Alice")},
},
{
Recipients: []string{"bob@example.com"},
Headers: paubox.MessageHeaders{From: "f@yourdomain.com", Subject: "Hi Bob"},
Content: paubox.MessageContent{PlainText: paubox.Ptr("Hello Bob")},
},
},
})
if err != nil {
log.Fatal(err)
}
for i, msg := range resp.Messages {
fmt.Printf("[%d] tracking ID: %s\n", i, msg.SourceTrackingID)
}
```
## Check delivery status
```go theme={null}
disp, err := client.GetEmailDisposition(ctx, sourceTrackingID)
```
`disp.Data.Message.MessageDeliveries` is a slice of per-recipient records:
```go theme={null}
for _, d := range disp.Data.Message.MessageDeliveries {
fmt.Printf("%s → %s (opened: %d)\n",
d.Recipient,
d.Status.DeliveryStatus,
d.OpenedCount,
)
}
```
Common `DeliveryStatus` values: `delivered`, `opened`, `failed`, `pending`.
## Dynamic templates
Templates use [Handlebars](https://handlebarsjs.com) syntax (`{{variable_name}}`).
### Create a template
```go theme={null}
tmpl, err := client.CreateTemplate(ctx, &paubox.CreateTemplateRequest{
Name: "appointment-confirmation",
Body: []byte(`Hello {{first_name}}, your appointment is on {{date}} at {{time}}.
`),
})
```
### List templates
```go theme={null}
list, err := client.ListTemplates(ctx)
for _, t := range list.Templates {
fmt.Println(t.ID, t.Name)
}
```
### Get a template
```go theme={null}
tmpl, err := client.GetTemplate(ctx, "template-id")
```
### Update a template
```go theme={null}
tmpl, err := client.UpdateTemplate(ctx, "template-id", &paubox.UpdateTemplateRequest{
Name: "appointment-confirmation-v2",
Body: []byte(`Updated body for {{first_name}}.
`),
})
```
### Delete a template
```go theme={null}
_, err = client.DeleteTemplate(ctx, "template-id")
```
### Send a templated message
```go theme={null}
resp, err := client.SendTemplatedMessage(ctx, &paubox.SendTemplatedMessageRequest{
TemplateName: "appointment-confirmation",
TemplateValues: map[string]any{
"first_name": "Jane",
"date": "2024-03-15",
"time": "2:00 PM",
},
Message: paubox.TemplatedMessage{
Recipients: []string{"jane@example.com"},
Headers: paubox.MessageHeaders{
From: "appointments@yourclinic.com",
Subject: "Your appointment is confirmed",
},
},
})
```
## Error handling
All methods return a typed `error`. Use `errors.As` to inspect details and `errors.Is` to match sentinels:
```go theme={null}
resp, err := client.SendMessage(ctx, req)
if err != nil {
// Match a specific condition
if errors.Is(err, paubox.ErrUnauthorized) {
log.Fatal("invalid API key")
}
if errors.Is(err, paubox.ErrRateLimit) {
log.Fatal("rate limited: back off and retry")
}
// Inspect the full error
var apiErr *paubox.PauboxError
if errors.As(err, &apiErr) {
fmt.Printf("HTTP %d: %s - %s (request ID: %s)\n",
apiErr.StatusCode, apiErr.Title, apiErr.Details, apiErr.RequestID)
}
log.Fatal(err)
}
```
### Sentinel errors
| Sentinel | HTTP status |
| ------------------------ | ----------- |
| `paubox.ErrBadRequest` | 400 |
| `paubox.ErrUnauthorized` | 401 |
| `paubox.ErrForbidden` | 403 |
| `paubox.ErrNotFound` | 404 |
| `paubox.ErrRateLimit` | 429 |
| `paubox.ErrServerError` | 5xx |
# Forms client
Source: https://docs.paubox.com/go-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Go SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Creating a client
```go theme={null}
fc, err := paubox.NewFormsClient()
if err != nil {
log.Fatal(err)
}
```
Optional configuration is available via `FormsOption` values:
| Option | Description |
| ------------------------------- | --------------------------------- |
| `paubox.WithFormsBaseURL(url)` | Override the Forms API base URL |
| `paubox.WithFormsHTTPClient(c)` | Supply a custom `*http.Client` |
| `paubox.WithFormsTimeout(d)` | Set a per-request timeout |
| `paubox.WithFormsRetry(cfg)` | Configure retry behavior |
| `paubox.WithFormsUserAgent(s)` | Append to the `User-Agent` header |
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```go theme={null}
ctx := context.Background()
form, err := fc.GetForm(ctx, "your-form-uuid")
if err != nil {
log.Fatal(err)
}
fmt.Println("Form:", form.Title)
for _, field := range form.FormJSON.Body {
fmt.Printf(" [%s] id=%s label=%s\n", field.Type, field.ID, field.Label)
}
```
The `Form` struct includes:
| Field | Description |
| ---------- | ------------------------------------------------------ |
| `Title` | Display name of the form |
| `FormJSON` | Parsed field schema (`FormJSON.Body` is `[]FormField`) |
| `HTML` | Rendered HTML for embedding |
| `CSS` | Associated stylesheet |
## Submit a form
```go theme={null}
_, err = fc.SubmitForm(ctx, "your-form-uuid", paubox.FormSubmission{
FormData: map[string]any{
"name": "Jane Smith",
"email": "jane@example.com",
"dob": "1990-01-15",
},
})
if err != nil {
log.Fatal(err)
}
```
### Submitting with file attachments
Attach files by base64-encoding their content:
```go theme={null}
import (
"encoding/base64"
"os"
)
fileBytes, err := os.ReadFile("consent.pdf")
if err != nil {
log.Fatal(err)
}
_, err = fc.SubmitForm(ctx, "your-form-uuid", paubox.FormSubmission{
FormData: map[string]any{
"name": "Jane Smith",
},
Attachments: []paubox.FormAttachment{
{
Name: "consent.pdf",
Content: base64.StdEncoding.EncodeToString(fileBytes),
},
},
})
```
The `FormAttachment` struct:
| Field | Type | Description |
| --------- | -------- | ----------------------------- |
| `Name` | `string` | File name including extension |
| `Content` | `string` | Base64-encoded file content |
# Paubox Go SDK
Source: https://docs.paubox.com/go-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Go applications using the official Paubox Go SDK.
The Paubox Go SDK is the official Go client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). It provides a type-safe, idiomatic Go interface for sending encrypted email, managing dynamic templates, tracking delivery, and submitting forms, with no external dependencies.
```bash theme={null}
go get github.com/paubox/paubox-go
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Send batches of up to 50 messages in a single API call
* Track delivery status and open/click engagement per recipient
* Create, update, and delete dynamic Handlebars templates
* Send templated messages with per-message variable substitution
* Retrieve Paubox Form schemas and submit form responses with file attachments
* Configure automatic retries with exponential backoff and jitter
## Requirements
* **Go** 1.22 or later
* No external runtime dependencies; the SDK uses only the Go standard library
## Get started
Create a client and send your first email in minutes.
Obtain and configure your API key.
Full reference for sending email, managing templates, and tracking delivery.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/go-sdk/quickstart
Install the Paubox Go SDK and send your first HIPAA compliant email in minutes.
Add the SDK to your Go module:
```bash theme={null}
go get github.com/paubox/paubox-go
```
The SDK requires Go 1.22 or later and has no external dependencies.
Export your Paubox API key as an environment variable:
```bash theme={null}
export PAUBOX_API_KEY="YOUR_API_KEY"
```
To obtain your API key, see [Authentication](/go-sdk/authentication).
Create a file `main.go` with the following code:
```go theme={null}
package main
import (
"context"
"fmt"
"log"
"os"
paubox "github.com/paubox/paubox-go"
)
func main() {
client, err := paubox.New(
os.Getenv("PAUBOX_API_KEY"),
)
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
resp, err := client.SendMessage(ctx, &paubox.SendMessageRequest{
Message: paubox.Message{
Recipients: []string{"recipient@example.com"},
Headers: paubox.MessageHeaders{
From: "sender@yourdomain.com",
Subject: "Your first Paubox email",
},
Content: paubox.MessageContent{
PlainText: paubox.Ptr("This message was sent with the Paubox Go SDK."),
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("Sent. Tracking ID:", resp.SourceTrackingID)
}
```
Run it:
```bash theme={null}
go run main.go
```
```
Sent. Tracking ID: abc123-def456-...
```
Use the tracking ID returned by `SendMessage` to check delivery:
```go theme={null}
disp, err := client.GetEmailDisposition(ctx, resp.SourceTrackingID)
if err != nil {
log.Fatal(err)
}
for _, d := range disp.Data.Message.MessageDeliveries {
fmt.Printf("%s → %s\n", d.Recipient, d.Status.DeliveryStatus)
}
```
```
recipient@example.com → delivered
```
## Next steps
* [Email client](/go-sdk/email-client): send batches, attach files, manage dynamic templates, handle errors
* [Forms client](/go-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/java-sdk/authentication
Configure your Paubox API credentials for the Java SDK.
## Obtain your API key
The Email API service requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Create config.properties
Create a `config.properties` file in your project directory:
```properties theme={null}
APIKEY=YOUR_API_KEY
```
## Load credentials at startup
Call `ConfigurationManager.getProperties` once before creating an `EmailService`. Pass the file path relative to your working directory or as an absolute path:
```java theme={null}
import com.paubox.config.ConfigurationManager;
ConfigurationManager.getProperties("config.properties");
```
The SDK reads `APIKEY` from the loaded properties and sets the `Authorization` header automatically on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## FormsService
`FormsService` requires no credentials and no config loading:
```java theme={null}
import com.paubox.service.FormsService;
FormsService formsService = new FormsService();
```
## Security notes
* Do not commit `config.properties` to source control. Add it to `.gitignore`.
* In production environments, consider reading credentials from system environment variables and writing them to the properties file at startup rather than storing them statically on disk.
# Email client
Source: https://docs.paubox.com/java-sdk/email-client
Full reference for the Paubox Java SDK email client: building messages, sending email, tracking delivery, and error handling.
## Instantiation
After loading credentials with `ConfigurationManager.getProperties(...)`, create the service:
```java theme={null}
import com.paubox.service.EmailService;
EmailService service = new EmailService();
```
## Building a message
Messages are built by setting properties on `Message`, `Header`, and `Content` objects, then wiring them together:
```java theme={null}
import com.paubox.data.*;
Header header = new Header();
header.setFrom("sender@yourdomain.com"); // required
header.setSubject("Your results are ready"); // required
header.setReplyTo("support@yourdomain.com"); // optional
Content content = new Content();
content.setPlainText("Plain text body."); // at least one required
content.setHtmlText("HTML body.
"); // optional; auto base64-encoded
Message message = new Message();
message.setRecipients(new String[]{"alice@example.com"}); // required
message.setHeader(header);
message.setContent(content);
message.setCc(new String[]{"manager@example.com"}); // optional
message.setBcc(new String[]{"audit@example.com"}); // optional
message.setAllowNonTLS(false); // optional; default false
message.setForceSecureNotification("true"); // optional
```
### Attachments
Base64-encode the file content before setting it on the attachment:
```java theme={null}
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.ArrayList;
byte[] fileBytes = Files.readAllBytes(Paths.get("report.pdf"));
String encoded = Base64.getEncoder().encodeToString(fileBytes);
Attachment attachment = new Attachment();
attachment.setFileName("report.pdf");
attachment.setContentType("application/pdf");
attachment.setContent(encoded);
ArrayList attachments = new ArrayList<>();
attachments.add(attachment);
message.setAttachments(attachments);
```
## Send a message
```java theme={null}
SendMessageResponse response = service.sendMessage(message);
System.out.println("Tracking ID: " + response.getSourceTrackingId());
```
The `SendMessageResponse` fields:
| Method | Description |
| ----------------------- | --------------------------------------------- |
| `getSourceTrackingId()` | Use to check delivery status |
| `getData()` | Raw response data |
| `getErrors()` | List of `Error` objects if the request failed |
## Check delivery status
```java theme={null}
GetEmailDispositionResponse disposition =
service.getEmailDisposition(response.getSourceTrackingId());
for (MessageDeliveries delivery :
disposition.getData().getMessage().getMessageDeliveries()) {
System.out.println(
delivery.getRecipient() + " → " +
delivery.getStatus().getDeliveryStatus()
);
}
```
Common `deliveryStatus` values: `delivered`, `opened`, `failed`, `pending`.
## Error handling
Both `sendMessage` and `getEmailDisposition` declare `throws Exception`. Wrap calls in a try/catch block:
```java theme={null}
try {
SendMessageResponse response = service.sendMessage(message);
System.out.println("Sent: " + response.getSourceTrackingId());
if (response.getErrors() != null && !response.getErrors().isEmpty()) {
for (Error error : response.getErrors()) {
System.err.println("Error " + error.getCode() + ": " + error.getTitle());
}
}
} catch (Exception e) {
System.err.println("Send failed: " + e.getMessage());
}
```
# Forms client
Source: https://docs.paubox.com/java-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Java SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Instantiation
```java theme={null}
import com.paubox.service.FormsService;
FormsService service = new FormsService();
```
No config loading is required.
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```java theme={null}
import com.paubox.data.Form;
Form form = service.getForm("your-form-uuid");
System.out.println("Title: " + form.getTitle());
System.out.println("HTML: " + form.getFormHtml());
```
Key `Form` getters:
| Method | Description |
| ---------------------- | ----------------------------------------- |
| `getTitle()` | Display name of the form |
| `getFormJson()` | Parsed field schema |
| `getFormHtml()` | Rendered HTML for embedding |
| `getFormCss()` | Associated stylesheet |
| `isActive()` | Whether the form is accepting submissions |
| `getSubmissionCount()` | Number of submissions received |
## Submit a form
```java theme={null}
import com.paubox.data.FormSubmissionRequest;
import java.util.HashMap;
import java.util.Map;
Map formData = new HashMap<>();
formData.put("first_name", "Jane");
formData.put("last_name", "Smith");
formData.put("email", "jane@example.com");
FormSubmissionRequest request = new FormSubmissionRequest();
request.setFormData(formData);
service.submitForm("your-form-uuid", request);
```
`submitForm` returns `void` on success (HTTP 201) and throws on failure.
### Submitting with file attachments
The maximum total request size is 250 MB.
```java theme={null}
import com.paubox.data.FormSubmissionAttachment;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.ArrayList;
import java.util.List;
byte[] fileBytes = Files.readAllBytes(Paths.get("consent.pdf"));
String encoded = Base64.getEncoder().encodeToString(fileBytes);
FormSubmissionAttachment attachment = new FormSubmissionAttachment();
attachment.setName("consent.pdf");
attachment.setContent(encoded);
List attachments = new ArrayList<>();
attachments.add(attachment);
FormSubmissionRequest request = new FormSubmissionRequest();
request.setFormData(formData);
request.setAttachments(attachments);
service.submitForm("your-form-uuid", request);
```
## Error handling
```java theme={null}
try {
Form form = service.getForm("your-form-uuid");
service.submitForm("your-form-uuid", request);
} catch (Exception e) {
System.err.println("Forms error: " + e.getMessage());
}
```
`getForm` and `submitForm` throw `Exception` on HTTP 400 (bad request) and 404 (form not found).
# Paubox Java SDK
Source: https://docs.paubox.com/java-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Java applications using the official Paubox Java SDK.
The Paubox Java SDK is the official Java client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). Add the JAR to your project and send encrypted, HIPAA compliant email with a few lines of Java.
## Installation
Download or clone the repository and add `Paubox.Email.API.jar` to your project's classpath.
**Eclipse / IntelliJ IDEA:** Right-click your project → Build Path / Dependencies → Add External JARs → select `Paubox.Email.API.jar`.
**Maven (local JAR):**
```xml theme={null}
com.paubox
paubox-email-api
1.0
system
${project.basedir}/lib/Paubox.Email.API.jar
```
Apache HttpClient and Jackson are bundled in the JAR; no additional dependencies are required.
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Add CC, BCC, attachments, and HTML or plain-text content
* Track delivery status and engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Java (any modern version)
## Get started
Add the JAR, configure credentials, and send your first email in minutes.
Set up your API key in a config.properties file.
Full reference for building messages, sending email, and checking delivery.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/java-sdk/quickstart
Add the Paubox Java SDK and send your first HIPAA compliant email in minutes.
Download `Paubox.Email.API.jar` from the repository and add it to your project's classpath. See [Installation](/java-sdk) for IDE and Maven instructions.
Create a `config.properties` file in your project:
```properties theme={null}
APIKEY=YOUR_API_KEY
```
To obtain your API key, see [Authentication](/java-sdk/authentication).
```java theme={null}
import com.paubox.service.EmailService;
import com.paubox.data.*;
import com.paubox.config.ConfigurationManager;
public class SendEmail {
public static void main(String[] args) throws Exception {
// Load credentials from config.properties
ConfigurationManager.getProperties("config.properties");
// Build the message
Header header = new Header();
header.setFrom("sender@yourdomain.com");
header.setSubject("Your first Paubox email");
Content content = new Content();
content.setPlainText("This message was sent with the Paubox Java SDK.");
Message message = new Message();
message.setRecipients(new String[]{"recipient@example.com"});
message.setHeader(header);
message.setContent(content);
// Send
EmailService service = new EmailService();
SendMessageResponse response = service.sendMessage(message);
System.out.println("Sent. Tracking ID: " + response.getSourceTrackingId());
}
}
```
```java theme={null}
GetEmailDispositionResponse disposition =
service.getEmailDisposition(response.getSourceTrackingId());
for (MessageDeliveries delivery :
disposition.getData().getMessage().getMessageDeliveries()) {
System.out.println(delivery.getRecipient()
+ " → " + delivery.getStatus().getDeliveryStatus());
}
```
## Next steps
* [Email client](/java-sdk/email-client): CC/BCC, attachments, error handling
* [Forms client](/java-sdk/forms-client): retrieve form schemas and submit responses
# Overview
Source: https://docs.paubox.com/marketing
Base URL, authentication, core concepts, and conventions for the Paubox Marketing API.
## Base URL
`https://api.paubox.com/v1/marketing`
## Authorization
Include an `Authorization` header with every request:
`Authorization: Token token=YOUR_API_KEY`
Replace `YOUR_API_KEY` with your API key. Generate your key on the [Paubox Marketing > Settings](https://next.paubox.com/marketing/settings) page (note: each API key is displayed only once upon creation):

## Example call
```bash theme={null}
curl -X POST \
https://api.paubox.com/v1/marketing/subscribers \
-H 'Authorization: Token token=YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"subscriber": {
"email": "recipient@example.com",
"first_name": "Jane",
"last_name": "Smith"
}
}'
```
## Core concepts
Four resources cover most of what the Marketing API does. Understanding how they relate makes the rest of the reference easier to navigate.
| Resource | What it is |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| **Campaign mailing** | The email itself — subject, HTML body, text body. Creating one only stores content; it sends nothing. |
| **Subscriber** | A person in your account, identified by email address. |
| **List** | Who receives a campaign. Either a *subscription list* (explicit membership) or a *dynamic list* (membership computed from saved filters). |
| **Subscription** | The link between one subscriber and one list. This is where a list-level opt-out is recorded. |
A **send** ties them together: it takes one campaign mailing, one list (or an explicit set of recipient addresses), and delivers the mailing to that audience. The same mailing can be sent more than once, and each send is tracked separately in analytics.
**Subscription lists vs. dynamic lists:**
Membership in a subscription list is explicit — you add and remove subscribers yourself. Membership in a dynamic list is derived from filters saved on the list, so it changes as your subscriber data changes. Endpoints that act on a list in bulk come in two variants for this reason: `bulk_global_*` for subscription lists, `dynamic_bulk_*` for dynamic lists.
## Send a campaign
[Create a campaign](/marketing/campaigns/create) with your subject and content. `subject` is required and must be unique within your account.
```bash theme={null}
curl -X POST \
https://api.paubox.com/v1/marketing/campaign_mailings \
-H 'Authorization: Token token=YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"campaign_mailing": {
"subject": "March newsletter",
"html_part": "Hello
",
"text_part": "Hello",
"template_type": "html"
}
}'
```
The response contains the new mailing's ID, which you will need for every step that follows:
```json theme={null}
{ "data": { "id": "123e4567-e89b-12d3-a456-426614174000" } }
```
An unsubscribe footer is appended to `html_part` automatically, so you do not need to add one yourself.
[Send a test email](/marketing/campaigns/send-test-email) to a single address to check rendering before you send to a list. The subject is prefixed with `[Test]`, and test sends are not recorded in analytics.
```bash theme={null}
curl -X GET \
'https://api.paubox.com/v1/marketing/campaign_mailings/123e4567-e89b-12d3-a456-426614174000/send_test_email?to_email=me@example.com' \
-H 'Authorization: Token token=YOUR_API_KEY'
```
A successful test send returns `204 No Content` with an empty body.
[Update the campaign](/marketing/campaigns/update) to change any field. Only the fields you send are modified.
[Send the campaign](/marketing/campaigns/send) to go out now, or [schedule it](/marketing/campaigns/schedule) for a future time. Both take the campaign mailing ID plus a target — a `subscription_list_id`, a `dynamic_list_id`, or an explicit list of `recipient_emails`.
**Sending is asynchronous.**
A successful response means the send was accepted and queued, not that delivery has finished. Track progress through the [campaign analytics](/marketing/analytics/campaigns) endpoints rather than the send response.
Use the analytics endpoints to see how the campaign performed: [send totals](/marketing/analytics/campaigns), [per-send results](/marketing/analytics/campaign-sends), [individual deliveries](/marketing/analytics/deliveries), and [tracking link engagement](/marketing/analytics/tracking-links).
**Tip:**
A brand-new Paubox Marketing account may be temporarily prevented from scheduling its first campaign as an anti-abuse measure. If you hit a `403` with a message about new accounts, contact [support@paubox.com](mailto:support@paubox.com) and they can clear it for you.
## Manage opt-ins and opt-outs
Paubox Marketing records two independent levels of opt-out, and it matters which one you use.
| Level | Where it lives | Effect |
| -------------- | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **List-level** | `unsubscribed_at` on the subscription | The subscriber stops receiving campaigns sent to that one list. They still receive campaigns sent to other lists. |
| **Global** | `opted_out_on` on the subscriber | The subscriber receives nothing, across every list. |
Which one an endpoint applies depends on whether you send `subscription_list_ids`:
* [`POST /subscriptions/unsubscribe`](/marketing/subscriptions/unsubscribe) **with** `subscription_list_ids` records a list-level opt-out.
* The same endpoint **without** `subscription_list_ids` records a global opt-out.
* [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe) always clears the global opt-out, because a subscriber who is subscribed to any list is by definition not globally unsubscribed.
Treat a global opt-out as permanent unless the recipient asks to be resubscribed. Re-subscribing someone who opted out, without a new request from them, is exactly the pattern that damages sending reputation and can put you out of compliance with anti-spam rules.
For a single subscriber and list you already have a subscription record for, [deleting the subscription](/marketing/subscriptions/delete) is the most direct route — it stamps `unsubscribed_at` and leaves everything else untouched.
### Acting on a whole list
When you want to opt out an entire list rather than a known set of subscribers, use the bulk endpoints. They accept a `from_subscription_list_id` and operate on everyone in that list, minus any `except_ids` you supply — useful for "select all except these" flows.
* Subscription lists: [bulk global subscribe](/marketing/subscriptions/bulk-global-subscribe) and [bulk global unsubscribe](/marketing/subscriptions/bulk-global-unsubscribe)
* Dynamic lists: [bulk subscribe](/marketing/subscriptions/dynamic-bulk-subscribe) and [bulk unsubscribe](/marketing/subscriptions/dynamic-bulk-unsubscribe)
These endpoints run in the background. See [Background jobs](#background-jobs) below.
## API conventions
### Identifiers
Records are identified by UUID. A `campaign_mailing_id`, `subscription_list_id`, or subscriber ID in a URL or request body is always the UUID form:
```
123e4567-e89b-12d3-a456-426614174000
```
See [Locating parameter values](/marketing/parameter-values) for where to find each one in the dashboard.
**One exception:**
[`POST /subscriptions`](/marketing/subscriptions/create) takes the subscriber's internal numeric ID rather than the UUID. To add a subscriber to a list using the UUID you already have, [create the subscriber](/marketing/subscribers/create) with a `subscription_list_id` instead, or use [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe).
### Response shapes
Most read endpoints return resources in JSON:API form — an `id`, a `type`, and the fields nested under `attributes`:
```json theme={null}
{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"type": "campaign_mailing",
"attributes": {
"subject": "March newsletter",
"created_at": "2026-03-01T12:00:00Z"
}
}
}
```
Write endpoints are terser. Creating or updating a campaign mailing returns only the ID, not the full record:
```json theme={null}
{ "data": { "id": "123e4567-e89b-12d3-a456-426614174000" } }
```
List and detail responses for the same resource do not always carry the same fields. Listing campaign mailings includes aggregate counts (`sent_count`, `delivered_count`, and so on) but omits the content; fetching a single one includes `html_part`, `text_part`, and `form_data` but omits the counts. Each reference page documents its own response.
### Errors
**Check the response body, not just the status code.**
Several Marketing API write endpoints return `200 OK` when a request fails validation, with the problem reported in an `errors` key instead of `data`. A client that branches only on HTTP status will treat these as successes.
A failed write looks like this:
```json theme={null}
{ "errors": ["Subject has already been taken"] }
```
Endpoints that behave this way include [creating](/marketing/campaigns/create) and [updating](/marketing/campaigns/update) a campaign mailing, [creating a subscription](/marketing/subscriptions/create), and the subscribe and unsubscribe endpoints. Treat the presence of `errors` as the failure signal, and fall back to the status code for authentication (`401`), missing records (`404`), and server errors (`500`).
### Pagination
List endpoints are paginated by default. Control it with:
| Parameter | Purpose |
| ------------ | ---------------------------------------------------------------------- |
| `items` | Records per page |
| `page` | Which page to return |
| `pagination` | Set to `false` to disable paging and return everything in one response |
Pagination metadata is returned in the response headers. Some endpoints additionally include a `page_info` object in the response body.
### Background jobs
Operations that can affect a large number of records do not run inline. Instead they queue a job and return its identifier immediately:
```json theme={null}
{ "data": { "jid": "a1b2c3d4e5f6a7b8c9d0e1f2" } }
```
A `jid` in the response means the work was accepted, not that it has finished. Bulk subscribe and unsubscribe across a whole list behave this way; supplying an explicit `subscriber_ids` array to those same endpoints processes the change inline and returns the affected subscribers instead.
### Dates
Dates are passed as ISO8601 strings with UTC timezone.
## Finding id parameters for subscription\_list\_id, campaign\_mailing\_id, etc.
## Community & support
Ask usage questions in the Paubox Community.
Propose features and improvements.
Never post PHI, recipient addresses, or message content in public threads. Account, billing, or anything sensitive goes to [support@paubox.com](mailto:support@paubox.com).
# Campaign sends table
Source: https://docs.paubox.com/marketing/analytics/campaign-sends
openapi2 GET /analytics/campaign_mailing_sends_table
Return json data information about campaigns sent
# Campaign analytics
Source: https://docs.paubox.com/marketing/analytics/campaigns
openapi2 GET /analytics/campaign_mailing_send_totals
Return json data statistics about campaigns sent
# Campaign deliveries table
Source: https://docs.paubox.com/marketing/analytics/deliveries
openapi2 GET /analytics/campaign_mailing_deliveries_table
Returns json data about campaign deliveries sent
# Subscribers by tracking link
Source: https://docs.paubox.com/marketing/analytics/subscribers-by-link
openapi2 GET /analytics/subscribers_by_tracking_link
Returns json data about subscribers for a specific tracking link
# Tracking links by unique link
Source: https://docs.paubox.com/marketing/analytics/tracking-links
openapi2 GET /analytics/tracking_links_by_unique_link
Returns json data about interactions with tracking links
# List campaign mailings
Source: https://docs.paubox.com/marketing/campaigns
openapi2 GET /campaign_mailings
Return json data about Paubox Marketing Campaign Emails
# Bulk delete campaigns
Source: https://docs.paubox.com/marketing/campaigns/bulk-delete
openapi2 DELETE /campaign_mailings_bulk_delete
Permanently deletes the campaign mailings identified by `campaign_mailing_ids`. There is no single-delete endpoint; delete one mailing by passing an array of one ID.
This is irreversible. Deleting a mailing also removes its associated delivery and send records.
Omitting `campaign_mailing_ids` is a no-op and still returns `200` with an empty object.
**Example curl command:**
```bash
curl -X DELETE \
https://api.paubox.com/v1/marketing/campaign_mailings_bulk_delete \
-H 'authorization: Token token=YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
"campaign_mailing_ids": ["123e4567-e89b-12d3-a456-426614174000"]
}'
```
# Create a campaign
Source: https://docs.paubox.com/marketing/campaigns/create
openapi2 POST /campaign_mailings
Creates a campaign mailing (a marketing email) for the authenticated customer. Creating a mailing only stores the content; use [send a campaign](/marketing/campaigns/send) or [schedule a campaign](/marketing/campaigns/schedule) to deliver it.
`subject` is required and must be unique within your account. An unsubscribe footer is appended to `html_part` automatically on save.
**Example curl command:**
```bash
curl -X POST \
https://api.paubox.com/v1/marketing/campaign_mailings \
-H 'authorization: Token token=YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
"campaign_mailing": {
"subject": "March newsletter",
"html_part": "Hello
",
"text_part": "Hello",
"template_type": "html"
}
}'
```
# Fetch a campaign
Source: https://docs.paubox.com/marketing/campaigns/get
openapi2 GET /campaign_mailings/{campaign_mailing_id}
Returns a single campaign mailing, including its content. Unlike the list endpoint, this response carries `html_part`, `text_part` and `form_data`, and omits the aggregate delivery counts.
# Schedule a campaign mailing
Source: https://docs.paubox.com/marketing/campaigns/schedule
openapi2 POST /campaign_mailing_schedules
Schedules a new email campaign mailing with specified details for delivery at a future time
# Send a campaign mailing
Source: https://docs.paubox.com/marketing/campaigns/send
openapi2 POST /campaign_mailing_sends
Send campaign email to recipients. The email message must be created within the Paubox Marketing web interface in order to use this to trigger a send.
**Note:** The campaign_mailing_id can be retrieved from the id= part of the URL when editing a marketing email. The subscription_list_id can be retrieved from the listId= part of the URL of any Contact List page.
# Send a test email
Source: https://docs.paubox.com/marketing/campaigns/send-test-email
openapi2 GET /campaign_mailings/{campaign_mailing_id}/send_test_email
Sends a one-off preview of the campaign mailing to a single address, so you can check rendering before sending to a list. The subject is prefixed with `[Test]`, and the message is sent from your account's default brand `from_email` and `from_name`.
Test sends are not recorded as a campaign send and do not appear in analytics.
**Example curl command:**
```bash
curl -X GET \
'https://api.paubox.com/v1/marketing/campaign_mailings/123e4567-e89b-12d3-a456-426614174000/send_test_email?to_email=me@example.com' \
-H 'authorization: Token token=YOUR_API_KEY'
```
# Update a campaign
Source: https://docs.paubox.com/marketing/campaigns/update
openapi2 PATCH /campaign_mailings/{campaign_mailing_id}
Updates a campaign mailing. Only the fields you send are changed. `PUT` is accepted as an equivalent to `PATCH`.
`subject` must stay unique within your account. The unsubscribe footer is reapplied to `html_part` on every save.
**Example curl command:**
```bash
curl -X PATCH \
https://api.paubox.com/v1/marketing/campaign_mailings/123e4567-e89b-12d3-a456-426614174000 \
-H 'authorization: Token token=YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
"campaign_mailing": {
"subject": "March newsletter (revised)"
}
}'
```
# List drip campaigns
Source: https://docs.paubox.com/marketing/drip-campaigns
openapi2 GET /drip_campaigns
Return json data about Drip Campaigns
# Get a drip campaign
Source: https://docs.paubox.com/marketing/drip-campaigns/get
openapi2 GET /drip_campaigns/{id}
Returns json data about a drip campaign
# Pause a drip campaign
Source: https://docs.paubox.com/marketing/drip-campaigns/pause
openapi2 POST /drip_campaigns/{id}/pause
Pauses a drip campaign
# Start a drip campaign
Source: https://docs.paubox.com/marketing/drip-campaigns/start
openapi2 POST /drip_campaigns/{id}/start
Starts a drip campaign
# Update a drip campaign
Source: https://docs.paubox.com/marketing/drip-campaigns/update
openapi2 PUT /drip_campaigns/{id}
Updates drip campaign attributes
# List subscription lists
Source: https://docs.paubox.com/marketing/lists
openapi2 GET /subscription_lists
Return json data about subscription lists
# Create a subscription list
Source: https://docs.paubox.com/marketing/lists/create
openapi2 POST /subscription_lists
Creates a subscription list record
# Delete a subscription list
Source: https://docs.paubox.com/marketing/lists/delete
openapi2 DELETE /subscription_lists/{subscription_list_id}
Deletes subscription list record.
**Note:** Cannot delete default/all contacts list.
# Update a subscription list
Source: https://docs.paubox.com/marketing/lists/update
openapi2 PATCH /subscription_lists/{subscription_list_id}
Updates a subscription list record
# Locating parameter values
Source: https://docs.paubox.com/marketing/parameter-values
How to find parameter values like campaign_mailing_id, subscription_list_id, and campaign_mailing_send_id in the Paubox Marketing web dashboard.
Some Marketing API endpoints require IDs that identify specific campaigns, subscription lists, or campaign sends. This page shows where to find each of those IDs in the Paubox Marketing dashboard.
All IDs can also be retrieved programmatically. Campaign mailings, subscription lists, and send records all include their `id` in API responses.
## campaign\_mailing\_id
A `campaign_mailing_id` uniquely identifies a marketing email (campaign mailing) in Paubox Marketing. It is required when [sending a campaign](/marketing/campaigns/send) or [scheduling a campaign](/marketing/campaigns/schedule), and is accepted as an optional filter on the [campaign deliveries table](/marketing/analytics/deliveries).
**To find your `campaign_mailing_id`:**
1. Log in to the Paubox Marketing dashboard.
2. Navigate to **Marketing Emails** (Campaigns).
3. Open or edit any campaign.
4. Copy the value of the `id=` parameter from your browser's address bar.
## subscription\_list\_id
A `subscription_list_id` uniquely identifies a subscription list. It is used when [creating subscribers](/marketing/subscribers/create), [listing subscribers](/marketing/subscribers/get), [bulk-deleting subscribers](/marketing/subscribers/bulk-delete), [sending](/marketing/campaigns/send) or [scheduling](/marketing/campaigns/schedule) a campaign to a specific list, [updating](/marketing/lists/update) or [deleting a list](/marketing/lists/delete), and [updating a drip campaign](/marketing/drip-campaigns/update).
**To find your `subscription_list_id`:**
1. Log in to the Paubox Marketing dashboard.
2. Navigate to **Lists**.
3. Open or edit any list.
4. Copy the UUID from your browser's address bar.
## campaign\_mailing\_send\_id
A `campaign_mailing_send_id` uniquely identifies a specific send of a campaign. It is required for [campaign analytics](/marketing/analytics/campaigns), [tracking links by unique link](/marketing/analytics/tracking-links), and [subscribers by tracking link](/marketing/analytics/subscribers-by-link), and is accepted as an optional filter on the [campaign deliveries table](/marketing/analytics/deliveries) and when [listing tracking links](/marketing/tracking-links).
**To find your `campaign_mailing_send_id`:**
1. Log in to the Paubox Marketing dashboard.
2. Navigate to **Analytics**.
3. Click a sent campaign.
4. Copy the value of the `selectedId=` parameter from your browser's address bar.
# List subscribers
Source: https://docs.paubox.com/marketing/subscribers
openapi2 GET /subscribers
Return json data about subscribers
# Bulk create subscribers
Source: https://docs.paubox.com/marketing/subscribers/bulk-create
openapi2 POST /subscribers_bulk_create
Creates one or more subscriber records. Adds to subscription list if specified, otherwise adds to all contacts.
# Bulk delete subscribers
Source: https://docs.paubox.com/marketing/subscribers/bulk-delete
openapi2 DELETE /subscribers_bulk_delete
Deletes one or more subscriber records. Removes from subscription list if specified, otherwise removed from all contacts.
**Example:**
```
DELETE /subscribers_bulk_delete?subscriber_ids=id1,id2,id3&subscription_list_id=list123
```
# Create a subscriber
Source: https://docs.paubox.com/marketing/subscribers/create
openapi2 POST /subscribers
Creates a subscriber record. Adds record to subscription list if specified otherwise adds to all contacts.
**Example curl command:**
```bash
curl -X POST \
https://api.paubox.com/v1/marketing/subscribers \
-H 'authorization: Token token=' \
-H 'content-type: application/json' \
-d '{
"subscriber": {
"email": "test@example.com",
"first_name": "test",
"last_name": "example"
}
}'
```
# Get a subscriber
Source: https://docs.paubox.com/marketing/subscribers/get
openapi2 GET /subscribers/{subscriber_id}
Return json data about subscriber
# Update a subscriber
Source: https://docs.paubox.com/marketing/subscribers/update
openapi2 PUT /subscribers/{subscriber_id}
Updates a subscriber record
# List subscriptions
Source: https://docs.paubox.com/marketing/subscriptions
openapi2 GET /subscriptions
Returns every subscription belonging to the authenticated customer. A subscription is the join between a subscriber and a subscription list (or a dynamic list), and it carries the `unsubscribed_at` timestamp that marks a list-level opt-out.
This endpoint is not paginated and returns raw subscription records.
# Bulk global subscribe
Source: https://docs.paubox.com/marketing/subscriptions/bulk-global-subscribe
openapi2 POST /subscriptions/bulk_global_subscribe
Clears the global opt-out for a whole subscription list without having to enumerate every subscriber.
This endpoint has two modes:
- **Explicit** — supply `subscriber_ids` and the request behaves exactly
like [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe),
returning the affected subscribers synchronously.
- **Select-all** — omit `subscriber_ids` and supply
`from_subscription_list_id`. Every subscriber matching `search` and
`filters` in that list, minus `except_ids`, is queued for processing in
the background and a Sidekiq job ID (`jid`) is returned immediately.
In select-all mode `from_subscription_list_id` is required and must be a subscription list UUID.
# Bulk global unsubscribe
Source: https://docs.paubox.com/marketing/subscriptions/bulk-global-unsubscribe
openapi2 POST /subscriptions/bulk_global_unsubscribe
Globally opts out every subscriber in a subscription list without having to enumerate them.
This endpoint has two modes:
- **Explicit** — supply `subscriber_ids` and the request behaves exactly
like [`POST /subscriptions/unsubscribe`](/marketing/subscriptions/unsubscribe),
returning the affected subscribers synchronously.
- **Select-all** — omit `subscriber_ids` and supply
`from_subscription_list_id`. Every subscriber matching `search` and
`filters` in that list, minus `except_ids`, is queued for processing in
the background and a Sidekiq job ID (`jid`) is returned immediately.
In select-all mode `from_subscription_list_id` is required and must be a subscription list UUID. Globally opting a subscriber out suppresses all future marketing email to them, across every list.
# Create a subscription
Source: https://docs.paubox.com/marketing/subscriptions/create
openapi2 POST /subscriptions
Subscribes an existing subscriber to an existing subscription list.
Creating a subscription fires any drip campaign configured with a `subscription_created` trigger on that list.
A subscriber may only be subscribed to a given list once; a duplicate request fails validation.
**Note on `subscriber_id`:** this field takes the subscriber's internal numeric ID, not the UUID returned by the subscriber endpoints. To add a subscriber to a list by UUID, use [`POST /subscribers`](/marketing/subscribers/create) with a `subscription_list_id`, or [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe) to re-subscribe an existing subscription.
**Example curl command:**
```bash
curl -X POST \
https://api.paubox.com/v1/marketing/subscriptions \
-H 'authorization: Token token=YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
"subscription": {
"subscription_list_id": "123e4567-e89b-12d3-a456-426614174000",
"subscriber_id": 4821
}
}'
```
# Unsubscribe a subscription
Source: https://docs.paubox.com/marketing/subscriptions/delete
openapi2 DELETE /subscriptions/{subscription_id}
Unsubscribes the subscriber from the list this subscription belongs to by stamping `unsubscribed_at`. The subscription record itself is kept, and the subscriber stays subscribed to every other list and is not globally opted out.
To re-subscribe, use [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe).
# Bulk subscribe a dynamic list
Source: https://docs.paubox.com/marketing/subscriptions/dynamic-bulk-subscribe
openapi2 POST /subscriptions/dynamic_bulk_subscribe
The dynamic list counterpart to [bulk global subscribe](/marketing/subscriptions/bulk-global-subscribe).
- **Explicit** — supply `subscriber_ids` and the request behaves exactly
like [`POST /subscriptions/subscribe`](/marketing/subscriptions/subscribe).
- **Select-all** — omit `subscriber_ids` and supply
`from_subscription_list_id`, which here is a **dynamic list** UUID. The
job resolves the list's current membership, subtracts `except_ids`, and
clears the global opt-out on the rest. A Sidekiq job ID (`jid`) is
returned immediately.
A dynamic list's membership is defined by the filters saved on the list itself, so any `filters` sent with the request is ignored in select-all mode.
# Bulk unsubscribe a dynamic list
Source: https://docs.paubox.com/marketing/subscriptions/dynamic-bulk-unsubscribe
openapi2 POST /subscriptions/dynamic_bulk_unsubscribe
The dynamic list counterpart to [bulk global unsubscribe](/marketing/subscriptions/bulk-global-unsubscribe).
- **Explicit** — supply `subscriber_ids` and the request behaves exactly
like [`POST /subscriptions/unsubscribe`](/marketing/subscriptions/unsubscribe).
- **Select-all** — omit `subscriber_ids` and supply
`from_subscription_list_id`, which here is a **dynamic list** UUID. The
job resolves the list's current membership, subtracts `except_ids`, and
globally opts out the rest. A Sidekiq job ID (`jid`) is returned
immediately.
A dynamic list's membership is defined by the filters saved on the list itself, so any `filters` sent with the request is ignored in select-all mode.
# Fetch a subscription
Source: https://docs.paubox.com/marketing/subscriptions/get
openapi2 GET /subscriptions/{subscription_id}
Returns a single subscription by its UUID.
# Subscribe subscribers
Source: https://docs.paubox.com/marketing/subscriptions/subscribe
openapi2 POST /subscriptions/subscribe
Re-subscribes one or more subscribers, identified by their UUIDs.
When `subscription_list_ids` is supplied, the matching subscriptions have their `unsubscribed_at` cleared. When it is omitted, only the global opt-out (`opted_out_on`) is cleared and list-level opt-outs are left untouched.
In both cases the global opt-out is cleared, because a subscriber who is subscribed to any list is by definition not globally unsubscribed.
**Example curl command:**
```bash
curl -X POST \
https://api.paubox.com/v1/marketing/subscriptions/subscribe \
-H 'authorization: Token token=YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
"subscriber_ids": ["8f14e45f-ceea-467a-9f2c-4b3d2a1e5c60"],
"subscription_list_ids": ["123e4567-e89b-12d3-a456-426614174000"]
}'
```
# Unsubscribe subscribers
Source: https://docs.paubox.com/marketing/subscriptions/unsubscribe
openapi2 POST /subscriptions/unsubscribe
Unsubscribes one or more subscribers, identified by their UUIDs.
When `subscription_list_ids` is supplied, the matching subscriptions are stamped with `unsubscribed_at` and the subscribers remain subscribed to every other list. When it is omitted, the subscribers are globally opted out by stamping `opted_out_on`, which suppresses all future marketing email to them.
**Example curl command:**
```bash
curl -X POST \
https://api.paubox.com/v1/marketing/subscriptions/unsubscribe \
-H 'authorization: Token token=YOUR_API_KEY' \
-H 'content-type: application/json' \
-d '{
"subscriber_ids": ["8f14e45f-ceea-467a-9f2c-4b3d2a1e5c60"],
"subscription_list_ids": ["123e4567-e89b-12d3-a456-426614174000"]
}'
```
# List tracking links
Source: https://docs.paubox.com/marketing/tracking-links
openapi2 GET /tracking_links
Return a list of json data for all tracking links associated to a campaign mailing send or campaign mailing delivery.
**Note:** The campaign_mailing_send_id is available from the Paubox Marketing interface analytics page. Click a sent campaign and the URL will show a selectedId.
If you pass in the campaign_mailing_send_id, it will return a list of every link in every email sent in the campaign and the tracking info.
# Authentication
Source: https://docs.paubox.com/mcp-server/authentication
How to obtain and pass credentials to the Paubox MCP Server.
The Paubox MCP Server authenticates against the Paubox Email API on your behalf. You provide your API key once when configuring the server; it is never sent to the AI model.
If you don't have one, [sign up for Paubox Email API](https://www.paubox.com/pricing/paubox-email-api). Free tier includes up to 300 emails per month.
Go to [Paubox Email API > Settings](https://next.paubox.com/emailapi/settings) and add the domain you will send from. Complete domain verification before generating an API key; unverified domains cannot send email.
From [Paubox Email API > Settings](https://next.paubox.com/emailapi/settings), click your domain, then press **Add API Key**. Give the key a description and save it immediately; it is displayed only once.
The server resolves the API key using the following priority order. Use whichever method fits your client:
**1. OAuth connector form (recommended for Claude.ai and Claude Desktop)**
Add `https://mcp.paubox.com` as a connector. The client presents a **Configure Paubox** form automatically. Enter your API key once; the client stores a short-lived encrypted Bearer token and sends it on every subsequent request.
**2. Custom request headers**
Some MCP clients let you set custom headers on the connector. Pass:
```
x-paubox-api-key: your_api_key
```
**3. Environment variables (stdio / Claude Code)**
```bash theme={null}
export PAUBOX_API_KEY=your_api_key
```
Claude Code / stdio config:
```json theme={null}
{
"mcpServers": {
"paubox": {
"command": "npx",
"args": ["-y", "@paubox/mcp@latest"],
"env": {
"PAUBOX_API_KEY": "your_api_key"
}
}
}
}
```
**4. Per-call tool parameters (any transport)**
Pass `apiKey` directly in each tool call. This takes the highest runtime priority and overrides any other credential source. Useful for scripted clients or one-off requests.
Never commit API keys to source control. Use environment variables or a secrets manager to inject credentials at runtime.
## Required permissions
| Tool | Credential required |
| :--------------------- | :------------------------------------ |
| `send_secure_email` | Paubox Email API key with send access |
| `check_email_status` | Paubox Email API key with read access |
| `validate_credentials` | Any valid Paubox Email API key |
| `get_form` | None (public endpoint) |
| `submit_form` | None (public endpoint) |
A single API key generated from the Paubox Email API settings page covers all authenticated tools. The `get_form` and `submit_form` tools call public Forms API endpoints and require no credentials.
# Paubox MCP Server
Source: https://docs.paubox.com/mcp-server/index
The Paubox MCP Server gives AI assistants HIPAA compliant email and secure form tools through the Model Context Protocol.
The Paubox MCP Server lets AI assistants send HIPAA compliant email and work with Paubox Forms by calling Paubox tools through the Model Context Protocol (MCP).
Once connected, a client like Claude, Claude Desktop, Cursor, or any other MCP-compatible host can send encrypted email, check delivery status, fetch Paubox Form definitions, and submit form responses. The server runs the Paubox Email API and Paubox Forms calls on your behalf. Your API key stays with the server process and is never shared with the AI model.
## What you can build with it
Healthcare teams use the Paubox MCP Server to:
* Draft and send encrypted appointment reminders, follow-ups, and care instructions from inside a clinical AI assistant
* Run an intake conversation that fetches a Paubox Form, collects answers, and submits the response without copying data between tools
* Add email delivery and status checks to multi-step agent workflows that span scheduling, billing, or patient outreach
## Available tools
| Tool | What it does |
| :--------------------- | :-------------------------------------------------------- |
| `send_secure_email` | Send a HIPAA compliant email through the Paubox Email API |
| `check_email_status` | Check delivery status for a previously sent message |
| `validate_credentials` | Verify that the configured API key is valid |
| `get_form` | Retrieve metadata for a Paubox Form |
| `submit_form` | Submit a response to a Paubox Form |
See [MCP tools](/mcp-server/tools) for the full parameter reference for each tool
## Compatible clients
Any client that follows the MCP specification can connect to the Paubox MCP Server, including:
* Claude (via Settings > Integrations on [claude.ai](http://claude.ai))
* Claude Desktop
* Cursor, Windsurf, and other MCP-aware IDEs
* Custom agents and scripts built on the MCP specification
The server supports two transports, so clients can pick whichever they natively use:
* HTTP at `https://mcp.paubox.com`: authenticate via the OAuth connector form, or set the `x-paubox-api-key` header
* stdio via `npx @paubox/mcp@latest` with `PAUBOX_API_KEY` in the environment
## How it handles HIPAA and credentials
Email sent through the Paubox MCP Server is encrypted by the Paubox Email API, the same HIPAA compliant transactional email service that Paubox provides to healthcare developers. Messages count against your Email API plan and appear in your Paubox dashboard alongside the rest of your API traffic.
Your API key is held by the MCP server process. It is not sent to the AI model and does not appear in tool calls or model context.
## Get started
Connect Claude, Claude Desktop, or any MCP client.
See parameters and responses for every tool.
Generate a Paubox Email API key and configure credentials.
Learn more about the underlying Paubox Email API
## FAQs
It is a Model Context Protocol server hosted at `https://mcp.paubox.com` that gives AI assistants access to the Paubox Email API and Paubox Forms. Agents can send HIPAA compliant email and submit secure intake forms as native tool calls.
Yes. Connect through Settings > Integrations on claude.ai, or add the server to `claude_desktop_config.json` for Claude Desktop. The [Quickstart](/mcp-server/quickstart) covers both setups.
No. It uses your existing [Paubox Email API](https://www.paubox.com/products/paubox-email-api) account. The Email API free tier covers 300 emails per month.
No. The key is held by the MCP server process. The assistant calls tools by name and never sees the credentials.
HTTP at `https://mcp.paubox.com`, and stdio via the `@paubox/mcp` npm package.
## Community & support
Ask usage questions in the Paubox Community.
Propose features and improvements.
Never post PHI, recipient addresses, or message content in public threads. Account, billing, or anything sensitive goes to [support@paubox.com](mailto:support@paubox.com).
# Quickstart
Source: https://docs.paubox.com/mcp-server/quickstart
Connect the Paubox MCP Server to Claude, Claude Desktop, Claude Code, or any MCP-compatible client.
**HIPAA Notice:** If you plan to send or reference PHI when using the Paubox MCP connector through Claude, you must have a Business Associate Agreement (BAA) in place with Anthropic. BAAs are available on Claude Team and Enterprise plans. Your Paubox subscription covers email delivery, it does not cover protected health information (PHI) processed within Claude's context window.
Before connecting, you need a Paubox Email API key. See [Authentication](/mcp-server/authentication) for how to generate one from the Paubox dashboard.
1. In [Claude.ai](https://claude.ai), open **Settings > Integrations**.
2. Click **Add connector** and enter the server URL:
```
https://mcp.paubox.com
```
3. A **Configure Paubox** form opens automatically. Enter your API key.
4. Click **Save**. Claude stores your API key as a secure token and sends it on every request.
Claude will discover the available Paubox tools automatically.
Open (or create) `claude_desktop_config.json` and add the `paubox` entry to `mcpServers`:
```json claude_desktop_config.json (HTTP, recommended) theme={null}
{
"mcpServers": {
"paubox": {
"url": "https://mcp.paubox.com"
}
}
}
```
```json claude_desktop_config.json (stdio) theme={null}
{
"mcpServers": {
"paubox": {
"command": "npx",
"args": ["-y", "@paubox/mcp@latest"],
"env": {
"PAUBOX_API_KEY": "YOUR_API_KEY"
}
}
}
}
```
The config file is located at:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
When using the HTTP config, Claude Desktop will open a browser window to the Configure Paubox form to collect your credentials. Restart Claude Desktop after saving.
Run this command once with your API key (no credential prompts afterward):
```bash theme={null}
claude mcp add paubox \
-e PAUBOX_API_KEY=your_api_key \
-- npx @paubox/mcp@latest
```
Claude Code spawns the package locally and injects the API key as an environment variable.
Ask Claude to verify your credentials:
> "Use the Paubox MCP server to validate my credentials."
Claude will call `validate_credentials` and confirm your API key is working. Then try sending a test email:
> "Send a test email to [me@example.com](mailto:me@example.com) from [sender@yourdomain.com](mailto:sender@yourdomain.com) with the subject 'Hello from MCP'."
Claude will call `send_secure_email` and return a `sourceTrackingId` you can use to check delivery status.
## Other MCP-compatible clients
Any client that supports the MCP specification can connect using either transport:
* **HTTP**: `https://mcp.paubox.com`; use OAuth (credential form in the client UI) or set `x-paubox-api-key` as a custom request header
* **stdio**: run `npx @paubox/mcp@latest` with `PAUBOX_API_KEY` set in the environment
# MCP tools
Source: https://docs.paubox.com/mcp-server/tools
Reference for all tools exposed by the Paubox MCP Server.
The Paubox MCP Server exposes five tools. Each tool maps to an underlying Paubox API operation and is available to any connected MCP client.
## send\_secure\_email
Sends a single HIPAA compliant email through the Paubox Email API. The sender address must belong to a domain you have verified in the Paubox dashboard.
| Parameter | Type | Required | Description |
| :------------------------ | :--------------- | :------- | :---------------------------------------------------------------------------------------------------- |
| `from` | string | Yes | Sender address. Must be on a verified Paubox domain. |
| `to` | array of strings | Yes | Recipient email addresses. |
| `subject` | string | Yes | Email subject line. |
| `message` | string | Yes | Message body. Sent as plain text; the server also generates an HTML version automatically. |
| `cc` | array of strings | No | CC recipients. |
| `bcc` | array of strings | No | BCC recipients. |
| `forceSecureNotification` | boolean | No | Force a secure notification regardless of recipient settings. Defaults to `false`. |
| `apiKey` | string | No | Paubox API key. HTTP transport only; overrides the credential resolved from the connector or headers. |
**Example payload**
```json theme={null}
{
"from": "provider@clinic.com",
"to": ["patient@example.com"],
"subject": "Your appointment summary",
"message": "Thank you for visiting us today."
}
```
**Response:** returns a `sourceTrackingId` string you can pass to `check_email_status`.
## check\_email\_status
Retrieves the current delivery status of a message sent via `send_secure_email`.
| Parameter | Type | Required | Description |
| :----------------- | :----- | :------- | :----------------------------------------------- |
| `sourceTrackingId` | string | Yes | The tracking ID returned by `send_secure_email`. |
| `apiKey` | string | No | Paubox API key. HTTP transport only. |
**Example payload**
```json theme={null}
{
"sourceTrackingId": "abc123def456"
}
```
**Response:** returns an object with delivery disposition details and a timestamp.
## validate\_credentials
Verifies that the Paubox API credentials are present and valid by making a live check against the Paubox API. Useful as a first step before sending email.
| Parameter | Type | Required | Description |
| :-------- | :----- | :------- | :--------------------------------------------------------------------------------------------------------------------------- |
| `apiKey` | string | No | Paubox API key. HTTP transport only; pass if you want to validate a specific API key rather than the one already configured. |
When connecting via stdio (Claude Code), the API key comes from an environment variable and no parameters are needed.
**Example payload**
```json theme={null}
{}
```
**Response:** returns a confirmation with a masked API key on success, or an error description if the API key is missing or invalid.
## get\_form
Available via HTTP transport only. Not available when using the stdio transport (Claude Code).
Retrieves the full definition of a Paubox Form, including its title, description, and field schema, so an agent can present the form questions in a conversation. No authentication is required for this tool.
| Parameter | Type | Required | Description |
| :-------- | :------------ | :------- | :----------------------------------- |
| `formId` | string (UUID) | Yes | UUID of the Paubox Form to retrieve. |
**Example payload**
```json theme={null}
{
"formId": "550e8400-e29b-41d4-a716-446655440000"
}
```
**Response:** returns a form object including `title`, `description`, `form_json` (field definitions), and metadata fields (`active`, `signable`, `submission_count`, `created_at`, `updated_at`).
## submit\_form
Available via HTTP transport only. Not available when using the stdio transport (Claude Code).
Submits a completed response to a Paubox Form. No authentication is required for this tool.
| Parameter | Type | Required | Description |
| :------------ | :--------------- | :------- | :------------------------------------------------------------------------------------------------------------------------ |
| `formId` | string (UUID) | Yes | UUID of the form being submitted. |
| `formData` | object | Yes | Key-value pairs matching the form's field schema (`form_json`). Structure varies per form. |
| `attachments` | array of objects | No | File attachments. Each object must include `name` (filename) and `content` (base64-encoded file). Max total size: 250 MB. |
**Example payload: text fields only**
```json theme={null}
{
"formId": "550e8400-e29b-41d4-a716-446655440000",
"formData": {
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com"
}
}
```
**Example payload: with attachment**
```json theme={null}
{
"formId": "550e8400-e29b-41d4-a716-446655440000",
"formData": {
"first_name": "Jane"
},
"attachments": [
{
"name": "consent.pdf",
"content": "JVBERi0xLjQ..."
}
]
}
```
**Response:** returns a success confirmation message on success.
# Authentication
Source: https://docs.paubox.com/node-sdk/authentication
Configure your Paubox API credentials for the Node.js SDK.
## Obtain your credentials
The Email API service requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Option 1: Environment variables (recommended)
Create a `.env` file in your project root:
```bash theme={null}
API_KEY=YOUR_API_KEY
```
The SDK loads this automatically via `dotenv`. Call the factory with no arguments:
```javascript theme={null}
const pbMail = require('paubox-node');
const service = pbMail.emailService();
```
## Option 2: Direct configuration
Pass the API key explicitly to the factory:
```javascript theme={null}
const service = pbMail.emailService({
apiKey: 'YOUR_API_KEY'
});
```
The service factory throws immediately if the API key is missing, so errors surface at startup rather than at send time.
The SDK sets the `Authorization` header automatically on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## Forms service
`formService` requires no credentials:
```javascript theme={null}
const formsService = pbMail.formService();
```
## Security notes
* Add `.env` to your `.gitignore`; never commit credentials to source control.
* In production, prefer your platform's secret management (environment variables injected by your hosting provider) over `.env` files.
# Email client
Source: https://docs.paubox.com/node-sdk/email-client
Full reference for the Paubox Node.js SDK email service: building messages, sending email, tracking delivery, dynamic templates, and error handling.
## Create the service
```javascript theme={null}
const pbMail = require('paubox-node');
// Read the API key from environment variables
const service = pbMail.emailService();
// Or pass the API key directly
const service = pbMail.emailService({ apiKey: 'YOUR_API_KEY' });
```
## Build a message
```javascript theme={null}
const message = pbMail.message({
from: 'sender@yourdomain.com', // required
to: ['alice@example.com'], // required; array of recipients
subject: 'Your results are ready', // optional
reply_to: 'support@yourdomain.com', // optional
cc: ['manager@example.com'], // optional
bcc: ['audit@example.com'], // optional
text_content: 'Plain text body.', // at least one of text/html required
html_content: 'HTML body.
', // optional; auto base64-encoded
allowNonTLS: false, // optional; default false
forceSecureNotification: false, // optional; default false
custom_headers: { 'X-Custom': 'value' },// optional; keys must start with X-
list_unsubscribe: '', // optional
list_unsubscribe_post: 'List-Unsubscribe=One-Click', // optional
attachments: [attachment] // optional; see below
});
```
### Attachments
```javascript theme={null}
const fs = require('fs');
const attachment = {
fileName: 'report.pdf',
contentType: 'application/pdf',
content: fs.readFileSync('report.pdf').toString('base64')
};
```
## Send a message
```javascript theme={null}
const response = await service.sendMessage(message);
console.log('Tracking ID:', response.sourceTrackingId);
```
## Send bulk messages
Send up to 50 messages in a single API call. Each message gets its own tracking ID.
```javascript theme={null}
const messages = [
pbMail.message({ from: 'f@yourdomain.com', to: ['alice@example.com'], subject: 'Hi Alice', text_content: 'Hello Alice' }),
pbMail.message({ from: 'f@yourdomain.com', to: ['bob@example.com'], subject: 'Hi Bob', text_content: 'Hello Bob' }),
];
const response = await service.sendBulkMessages(messages);
for (const msg of response.messages) {
console.log('Tracking ID:', msg.sourceTrackingId);
}
```
## Check delivery status
```javascript theme={null}
const disposition = await service.getEmailDisposition(sourceTrackingId);
for (const delivery of disposition.data.message.message_deliveries) {
console.log(`${delivery.recipient} → ${delivery.status.deliveryStatus}`);
}
```
Common `deliveryStatus` values: `delivered`, `opened`, `failed`, `pending`. The `openedStatus` field defaults to `"unopened"` if not yet set.
## Dynamic templates
Templates use [Handlebars](https://handlebarsjs.com) syntax (`{{variable_name}}`). Template content can be a string, `Buffer`, or readable `Stream`.
### Create a template
```javascript theme={null}
const fs = require('fs');
const response = await service.createDynamicTemplate(
'appointment-confirmation',
fs.createReadStream('templates/appointment.html')
);
```
### List templates
```javascript theme={null}
const templates = await service.listDynamicTemplates();
for (const t of templates) {
console.log(t.id, t.name);
}
```
### Get a template
```javascript theme={null}
const template = await service.getDynamicTemplate(templateId);
```
### Update a template
Pass `null` for fields you want to leave unchanged:
```javascript theme={null}
await service.updateDynamicTemplate(
templateId,
'appointment-confirmation-v2', // new name, or null to keep current
null // content unchanged
);
```
### Delete a template
```javascript theme={null}
await service.deleteDynamicTemplate(templateId);
```
### Send a templated message
```javascript theme={null}
const message = pbMail.templatedMessage({
from: 'appointments@yourclinic.com',
to: ['jane@example.com'],
subject: 'Your appointment is confirmed',
template_name: 'appointment-confirmation',
template_values: {
first_name: 'Jane',
date: '2024-03-15',
time: '2:00 PM'
}
});
const response = await service.sendTemplatedMessage(message);
```
## Error handling
All service methods return Promises and reject on API errors. Use `try/catch` in async functions:
```javascript theme={null}
try {
const response = await service.sendMessage(message);
console.log('Sent:', response.sourceTrackingId);
} catch (err) {
console.error('Send failed:', err.message);
}
```
# Forms client
Source: https://docs.paubox.com/node-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Node.js SDK.
The Forms service does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Create the service
```javascript theme={null}
const pbMail = require('paubox-node');
const formsService = pbMail.formService();
```
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```javascript theme={null}
const form = await formsService.getForm('your-form-uuid');
console.log('Title:', form.title);
console.log('HTML:', form.form_html);
```
Key fields in the response:
| Field | Description |
| ------------------ | ----------------------------------------- |
| `title` | Display name of the form |
| `form_json` | Parsed field schema |
| `form_html` | Rendered HTML for embedding |
| `form_css` | Associated stylesheet |
| `active` | Whether the form is accepting submissions |
| `submission_count` | Number of submissions received |
The Promise rejects with a 404 error if the form UUID is invalid.
## Submit a form
```javascript theme={null}
await formsService.submitForm('your-form-uuid', {
first_name: 'Jane',
last_name: 'Smith',
email: 'jane@example.com'
});
// Resolves to null on success (HTTP 201)
```
### Submitting with file attachments
The maximum total request size is 250 MB.
```javascript theme={null}
const fs = require('fs');
const attachments = [
{
name: 'consent.pdf',
content: fs.readFileSync('consent.pdf').toString('base64')
}
];
await formsService.submitForm(
'your-form-uuid',
{ first_name: 'Jane' },
attachments
);
```
Each attachment is an object with two fields:
| Field | Description |
| --------- | ----------------------------- |
| `name` | File name including extension |
| `content` | Base64-encoded file content |
## Error handling
```javascript theme={null}
try {
const form = await formsService.getForm('your-form-uuid');
await formsService.submitForm('your-form-uuid', formData);
} catch (err) {
console.error('Forms error:', err.message);
}
```
The Promise rejects on HTTP 400 (bad request) and 404 (form not found).
# Paubox Node.js SDK
Source: https://docs.paubox.com/node-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Node.js applications using the official Paubox Node.js SDK.
The Paubox Node.js SDK is the official JavaScript client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). All service methods return Promises, making them compatible with `async/await` and promise chains.
```bash theme={null}
npm install --save paubox-node
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Send batches of up to 50 messages in a single API call
* Create, update, and delete dynamic Handlebars templates
* Send templated messages with per-message variable substitution
* Track delivery status and engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Node.js v22 or later
## Get started
Install, configure credentials, and send your first email in minutes.
Set up your API key via environment variables or direct config.
Full reference for sending email, managing templates, and tracking delivery.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/node-sdk/quickstart
Install the Paubox Node.js SDK and send your first HIPAA compliant email in minutes.
```bash theme={null}
npm install --save paubox-node
```
Create a `.env` file in your project root:
```bash theme={null}
API_KEY=YOUR_API_KEY
```
The SDK loads this automatically. To obtain your API key, see [Authentication](/node-sdk/authentication).
Create `send.js`:
```javascript theme={null}
const pbMail = require('paubox-node');
async function main() {
const service = pbMail.emailService();
const message = pbMail.message({
from: 'sender@yourdomain.com',
to: ['recipient@example.com'],
subject: 'Your first Paubox email',
text_content: 'This message was sent with the Paubox Node.js SDK.'
});
const response = await service.sendMessage(message);
console.log('Sent. Tracking ID:', response.sourceTrackingId);
}
main().catch(console.error);
```
Run it:
```bash theme={null}
node send.js
```
```
Sent. Tracking ID: abc123-def456-...
```
```javascript theme={null}
const disposition = await service.getEmailDisposition(response.sourceTrackingId);
for (const delivery of disposition.data.message.message_deliveries) {
console.log(`${delivery.recipient} → ${delivery.status.deliveryStatus}`);
}
```
```
recipient@example.com → delivered
```
## Next steps
* [Email client](/node-sdk/email-client): bulk send, attachments, dynamic templates, error handling
* [Forms client](/node-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/perl-sdk/authentication
Configure your Paubox API credentials for the Perl SDK.
## Obtain your API key
The Email API client requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Create a config.cfg file
Create a `config.cfg` file in your project directory:
```
API_KEY = YOUR_API_KEY
```
The SDK reads this file automatically via `Config::General` when you instantiate the client; no constructor arguments are needed:
```perl theme={null}
my $service = Paubox_Email_SDK->new();
```
The constructor dies immediately if `API_KEY` is missing from the config file, so credential errors are caught at startup.
The SDK sets the `Authorization` header on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## Forms client
`Paubox_Forms_SDK` requires no credentials. Form submissions are associated with the form ID, not an API key.
```perl theme={null}
my $forms = Paubox_Forms_SDK->new();
```
## Security notes
* Do not commit `config.cfg` to source control. Add it to your `.gitignore`.
* The SDK does not expose credentials in error messages or logs.
# Email client
Source: https://docs.paubox.com/perl-sdk/email-client
Full reference for the Paubox Perl SDK Email API client: building messages, sending email, tracking delivery, and error handling.
## Instantiation
```perl theme={null}
my $service = Paubox_Email_SDK->new();
```
Credentials are read automatically from `config.cfg` (see [Authentication](/perl-sdk/authentication)). The constructor dies if `API_KEY` is missing.
## Building a message
Messages are created with a single constructor call using named parameters:
```perl theme={null}
my $msg = Paubox_Email_SDK::Message->new(
'from' => 'sender@yourdomain.com', # required
'to' => ['alice@example.com'], # required; arrayref
'subject' => 'Your results are ready', # required
'text_content' => 'Plain text body.', # at least one required
'html_content' => 'HTML body.
', # optional; auto base64-encoded
'replyTo' => 'support@yourdomain.com', # optional
'cc' => ['manager@example.com'], # optional; arrayref
'bcc' => ['audit@example.com'], # optional; arrayref
'allowNonTLS' => 0, # optional; default 0
'forceSecureNotification' => 'true', # optional; "true"/"false"
'attachments' => [$attachment], # optional; arrayref of hashrefs
);
```
### Attachments
Each attachment is a hashref with three keys. Use `MIME::Base64` to encode the file content:
```perl theme={null}
use MIME::Base64;
use String::Util qw(trim);
my $attachment = {
fileName => 'report.pdf',
contentType => 'application/pdf',
content => trim(encode_base64(do { local $/; open my $fh, '<:raw', 'report.pdf' or die $!; <$fh> })),
};
my $msg = Paubox_Email_SDK::Message->new(
'from' => 'sender@yourdomain.com',
'to' => ['alice@example.com'],
'subject' => 'Report attached',
'text_content'=> 'See attachment.',
'attachments' => [$attachment],
);
```
`String::Util::trim` removes the trailing newline that `encode_base64` appends.
## Send a message
```perl theme={null}
my $response = $service->sendMessage($msg);
```
The response is a raw JSON string. Decode it to access fields:
```perl theme={null}
use JSON;
my $json = decode_json($response);
print "Tracking ID: " . $json->{sourceTrackingId} . "\n";
```
## Check delivery status
```perl theme={null}
my $disposition = $service->getEmailDisposition($sourceTrackingId);
my $json = decode_json($disposition);
for my $delivery (@{ $json->{data}{message}{message_deliveries} }) {
print $delivery->{recipient} . " -> " . $delivery->{status}{deliveryStatus} . "\n";
}
```
Common `deliveryStatus` values: `delivered`, `opened`, `failed`, `pending`.
## Error handling
Both `sendMessage` and `getEmailDisposition` use `TryCatch` internally and die on API errors. Wrap calls in an `eval` block to catch failures:
```perl theme={null}
eval {
my $response = $service->sendMessage($msg);
print "Sent: $response\n";
};
if ($@) {
print "Error: $@\n";
}
```
# Forms client
Source: https://docs.paubox.com/perl-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Perl SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Instantiation
```perl theme={null}
use Paubox_Forms_SDK;
my $forms = Paubox_Forms_SDK->new();
```
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```perl theme={null}
my $response = $forms->getForm("your-form-uuid");
```
The response is a raw JSON string. Decode it to access fields:
```perl theme={null}
use JSON;
my $form = decode_json($response);
print "Title: " . $form->{title} . "\n";
print "HTML: " . $form->{form_html} . "\n";
```
Key fields in the response:
| Field | Description |
| ----------- | ----------------------------------------- |
| `title` | Display name of the form |
| `form_json` | Parsed field schema |
| `form_html` | Rendered HTML for embedding |
| `form_css` | Associated stylesheet |
| `active` | Whether the form is accepting submissions |
`getForm` dies if the form UUID is invalid or the form is not found.
## Submit a form
```perl theme={null}
my $result = $forms->submitForm(
"your-form-uuid",
{ first_name => "Jane", last_name => "Smith", email => "jane@example.com" }
);
# $result is an empty string on success (HTTP 201)
```
`submitForm` dies on failure with a message containing the HTTP status code and response body.
### Submitting with file attachments
The maximum total request size is 250 MB.
```perl theme={null}
use MIME::Base64;
open(my $fh, '<:raw', 'consent.pdf') or die "Cannot open file: $!";
local $/;
my $encoded = encode_base64(<$fh>);
close($fh);
my $result = $forms->submitForm(
"your-form-uuid",
{ first_name => "Jane" },
[ { name => "consent.pdf", content => $encoded } ]
);
```
Each attachment in the arrayref is a hashref with two keys:
| Key | Description |
| --------- | ----------------------------- |
| `name` | File name including extension |
| `content` | Base64-encoded file content |
## Error handling
Wrap calls in `eval` to catch failures:
```perl theme={null}
eval {
my $form = $forms->getForm("your-form-uuid");
my $result = $forms->submitForm("your-form-uuid", \%formData);
};
if ($@) {
print "Error: $@\n";
}
```
# Paubox Perl SDK
Source: https://docs.paubox.com/perl-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Perl applications using the official Paubox Perl SDK.
The Paubox Perl SDK is the official Perl client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). Install it from CPAN and send encrypted, HIPAA compliant email with a few lines of Perl.
```bash theme={null}
cpanm Paubox_Email_SDK
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Add CC, BCC, attachments, and HTML or plain-text content
* Track delivery status and engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Perl
* [cpanminus](https://metacpan.org/pod/App::cpanminus) (`cpanm`)
* Dependencies installed automatically: `JSON`, `Config::General`, `REST::Client`, `TryCatch`, `String::Util`, `MIME::Base64`
## Get started
Install, configure credentials, and send your first email in minutes.
Set up your API key via a config.cfg file.
Full reference for sending email and checking delivery status.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/perl-sdk/quickstart
Install the Paubox Perl SDK and send your first HIPAA compliant email in minutes.
Install from CPAN using cpanminus:
```bash theme={null}
cpanm Paubox_Email_SDK
```
Create a `config.cfg` file in your project directory:
```
API_KEY = YOUR_API_KEY
```
The SDK reads this file automatically. To obtain credentials, see [Authentication](/perl-sdk/authentication).
Create a file `send.pl`:
```perl theme={null}
use Paubox_Email_SDK;
my $msg = Paubox_Email_SDK::Message->new(
'from' => 'sender@yourdomain.com',
'to' => ['recipient@example.com'],
'subject' => 'Your first Paubox email',
'text_content' => 'This message was sent with the Paubox Perl SDK.',
);
my $service = Paubox_Email_SDK->new();
my $response = $service->sendMessage($msg);
print "Response: $response\n";
```
Run it:
```bash theme={null}
perl send.pl
```
The response is a JSON string containing `sourceTrackingId`:
```json theme={null}
{"sourceTrackingId":"abc123-def456-...","data":{"message":{"id":"..."}}}
```
Pass the tracking ID to `getEmailDisposition`:
```perl theme={null}
use JSON;
my $json = decode_json($response);
my $tracking = $json->{sourceTrackingId};
my $disposition = $service->getEmailDisposition($tracking);
print "Disposition: $disposition\n";
```
The response JSON includes `message_deliveries` with per-recipient status.
## Next steps
* [Email client](/perl-sdk/email-client): add HTML, attachments, CC/BCC, and handle errors
* [Forms client](/perl-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/php-sdk/authentication
Configure your Paubox API key for the PHP SDK.
## Obtain your API key
The Email API client requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Create a .env file
Add your API key to a `.env` file at your project root:
```bash theme={null}
PAUBOX_API_KEY=YOUR_API_KEY
```
The SDK reads these variables automatically on instantiation; no constructor arguments are needed:
```php theme={null}
$paubox = new Paubox\Paubox();
```
The SDK sets the `Authorization` header on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## Forms client
`Paubox\PauboxForms` requires no credentials. Form submissions are associated with the form ID, not an API key.
```php theme={null}
$forms = new Paubox\PauboxForms();
```
## Security notes
* Never commit `.env` files to source control. Add `.env` to your `.gitignore`.
* The SDK does not expose credentials in error messages or logs.
# Email client
Source: https://docs.paubox.com/php-sdk/email-client
Full reference for the Paubox PHP SDK Email API client: building messages, sending email, tracking delivery, and error handling.
## Instantiation
```php theme={null}
$paubox = new Paubox\Paubox();
```
Your API key is read automatically from the `.env` file (see [Authentication](/php-sdk/authentication)). No constructor arguments are needed.
## Building a message
A message is assembled from three objects (`Header`, `Content`, and `Message`) plus optional `Attachment` objects.
### Header
```php theme={null}
$header = new Paubox\Mail\Header();
$header->setSubject("Your results are ready"); // required
$header->setFrom("sender@yourdomain.com"); // required
$header->setReplyTo("support@yourdomain.com"); // optional
```
### Content
```php theme={null}
$content = new Paubox\Mail\Content();
$content->setPlainText("Plain text body."); // at least one required
$content->setHtmlText("HTML body.
"); // optional; auto base64-encoded by SDK
```
### Message
```php theme={null}
$message = new Paubox\Mail\Message();
$message->setHeader($header); // required
$message->setContent($content); // required
$message->setRecipients(["alice@example.com"]); // required
$message->setCc(["manager@example.com"]); // optional
$message->setBcc(["audit@example.com"]); // optional
$message->setAttachments([$attachment]); // optional; see below
$message->setAllowNonTLS(false); // optional; default false
$message->setForceSecureNotification("true"); // optional
```
### Attachments
```php theme={null}
$attachment = new Paubox\Mail\Attachment();
$attachment->setFileName("report.pdf");
$attachment->setContentType("application/pdf");
$attachment->setContent(base64_encode(file_get_contents("/path/to/report.pdf")));
$message->setAttachments([$attachment]);
```
## Send a message
```php theme={null}
$response = $paubox->sendMessage($message);
```
The response is a `stdClass` object:
| Property | Description |
| ------------------ | ---------------------------------------- |
| `sourceTrackingId` | Tracking ID for checking delivery status |
| `data` | Raw response data |
| `errors` | Array of error strings, if any |
```php theme={null}
echo $response->sourceTrackingId;
```
## Check delivery status
```php theme={null}
$disposition = $paubox->getEmailDisposition($sourceTrackingId);
```
`$disposition->data->message->message_deliveries` is an array of per-recipient objects:
```php theme={null}
foreach ($disposition->data->message->message_deliveries as $delivery) {
echo $delivery->recipient . " → " . $delivery->status->deliveryStatus . PHP_EOL;
}
```
Common `deliveryStatus` values: `delivered`, `opened`, `failed`, `pending`.
## Error handling
Both `sendMessage` and `getEmailDisposition` throw `\Exception` on failure. Wrap calls in a try/catch block:
```php theme={null}
try {
$response = $paubox->sendMessage($message);
echo "Sent. Tracking ID: " . $response->sourceTrackingId . PHP_EOL;
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
}
```
`sendMessage` throws if the `Header` or `Content` is null, or if the API response cannot be parsed. `getEmailDisposition` throws if the response cannot be parsed.
# Forms client
Source: https://docs.paubox.com/php-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the PHP SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Instantiation
```php theme={null}
$forms = new Paubox\PauboxForms();
```
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```php theme={null}
$form = $forms->getForm("your-form-uuid");
echo $form->title . PHP_EOL;
echo $form->form_html . PHP_EOL;
print_r($form->form_json);
```
The returned `stdClass` includes:
| Property | Description |
| ------------------ | ----------------------------------------- |
| `title` | Display name of the form |
| `form_json` | Parsed field schema |
| `form_html` | Rendered HTML for embedding |
| `form_css` | Associated stylesheet |
| `active` | Whether the form is accepting submissions |
| `submission_count` | Number of submissions received |
`getForm` throws `\Exception` if the form is not found or the response is invalid.
## Submit a form
```php theme={null}
$submission = new Paubox\Forms\FormSubmission();
$submission->setFormData([
"first_name" => "Jane",
"last_name" => "Smith",
"email" => "jane@example.com",
]);
$result = $forms->submitForm("your-form-uuid", $submission);
// $result === true on success
```
`submitForm` returns `true` on a successful submission (HTTP 201) and throws `\Exception` on failure with a message containing the HTTP status code and response body.
### Submitting with file attachments
The maximum total request size is 250 MB.
```php theme={null}
$attachment = new Paubox\Forms\FormAttachment();
$attachment->setName("consent.pdf");
$attachment->setContent(base64_encode(file_get_contents("/path/to/consent.pdf")));
$submission = new Paubox\Forms\FormSubmission();
$submission->setFormData(["first_name" => "Jane"]);
$submission->setAttachments([$attachment]);
$result = $forms->submitForm("your-form-uuid", $submission);
```
The `FormAttachment` fields:
| Field | Description |
| --------- | ----------------------------- |
| `name` | File name including extension |
| `content` | Base64-encoded file content |
## Error handling
Wrap calls in a try/catch block:
```php theme={null}
try {
$form = $forms->getForm("your-form-uuid");
$result = $forms->submitForm("your-form-uuid", $submission);
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
}
```
# Paubox PHP SDK
Source: https://docs.paubox.com/php-sdk/index
Send HIPAA compliant email and submit Paubox Forms from PHP applications using the official Paubox PHP SDK.
The Paubox PHP SDK is the official PHP client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). Install it with Composer and send encrypted, HIPAA compliant email with a few lines of PHP.
```bash theme={null}
composer require paubox/paubox-php
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Add CC, BCC, attachments, and HTML or plain-text content
* Track delivery status and engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* PHP (any recent version)
* [Composer](https://getcomposer.org)
* Dependencies installed automatically: `nategood/httpful` and `vlucas/phpdotenv`
## Get started
Install, configure your API key, and send your first email in minutes.
Set up your API key via a .env file.
Full reference for sending email and checking delivery status.
Retrieve form schemas and submit responses.
## Version note
The current stable release (v1) covers sending email and the Forms API. A v2 beta on the `sdk-generation/v2.0.0-beta` branch adds bulk sending and dynamic templates. This documentation covers v1.
# Quickstart
Source: https://docs.paubox.com/php-sdk/quickstart
Install the Paubox PHP SDK and send your first HIPAA compliant email in minutes.
Add the SDK to your project with Composer:
```bash theme={null}
composer require paubox/paubox-php
```
Create a `.env` file in your project root:
```bash theme={null}
PAUBOX_API_KEY=YOUR_API_KEY
```
The SDK loads this value automatically. To obtain your API key, see [Authentication](/php-sdk/authentication).
Create a file `send.php`:
```php theme={null}
setSubject("Your first Paubox email");
$header->setFrom("sender@yourdomain.com");
$content = new Paubox\Mail\Content();
$content->setPlainText("This message was sent with the Paubox PHP SDK.");
$message->setHeader($header);
$message->setContent($content);
$message->setRecipients(["recipient@example.com"]);
$response = $paubox->sendMessage($message);
echo "Sent. Tracking ID: " . $response->sourceTrackingId . PHP_EOL;
```
Run it:
```bash theme={null}
php send.php
```
```
Sent. Tracking ID: abc123-def456-...
```
Use the tracking ID to check delivery:
```php theme={null}
$disposition = $paubox->getEmailDisposition($response->sourceTrackingId);
foreach ($disposition->data->message->message_deliveries as $delivery) {
echo $delivery->recipient . " → " . $delivery->status->deliveryStatus . PHP_EOL;
}
```
```
recipient@example.com → delivered
```
## Next steps
* [Email client](/php-sdk/email-client): add HTML, attachments, CC/BCC, and handle errors
* [Forms client](/php-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/python-sdk/authentication
Configure your Paubox API credentials for the Python SDK.
## Obtain your credentials
The Email API client requires two values:
* **API key**: a secret token that authenticates your requests
* **API host**: the API endpoint URL, `https://api.paubox.com/v1`
Your API key is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Environment variables (recommended)
Set these in your shell or a `.env` file loaded by your application:
```bash theme={null}
PAUBOX_API_KEY=YOUR_API_KEY
PAUBOX_HOST=https://api.paubox.com/v1
```
The SDK reads these automatically; no constructor arguments needed:
```python theme={null}
from paubox import PauboxApiClient
client = PauboxApiClient()
```
## Constructor parameters
Pass credentials directly if you prefer not to use environment variables:
```python theme={null}
client = PauboxApiClient(
api_key = 'YOUR_API_KEY',
host = 'https://api.paubox.com/v1'
)
```
The SDK sets the `Authorization` header automatically on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## Forms client
`PauboxFormsClient` requires no credentials:
```python theme={null}
from paubox import PauboxFormsClient
forms_client = PauboxFormsClient()
```
## Security notes
* Never hard-code credentials in source files.
* The SDK does not log API keys or request bodies.
# Email client
Source: https://docs.paubox.com/python-sdk/email-client
Full reference for the Paubox Python SDK email client: composing messages, sending email, tracking delivery, and error handling.
## Instantiation
```python theme={null}
from paubox import PauboxApiClient
# Read credentials from environment variables
client = PauboxApiClient()
# Or pass credentials directly
client = PauboxApiClient(
api_key = 'YOUR_API_KEY',
host = 'https://api.paubox.com/v1'
)
```
See [Authentication](/python-sdk/authentication) for both approaches.
## Composing a message
Use the `Mail` helper to compose messages. It handles formatting and automatically base64-encodes HTML content before sending.
```python theme={null}
from paubox.helpers.mail import Mail
mail = Mail(
from_ = 'sender@yourdomain.com', # required
subject = 'Your results are ready', # required
recipients = ['alice@example.com'], # required; list of email strings
content = { # required; at least one key
'text/plain': 'Plain text body.',
'text/html': 'HTML body.
' # auto base64-encoded
},
optional_headers = { # optional
'reply_to': 'support@yourdomain.com',
'cc': ['manager@example.com'],
'bcc': 'audit@example.com', # str or list
'allowNonTLS': False,
'forceSecureNotification': False,
'attachments': [attachment]
}
)
```
Call `mail.get()` to get the formatted dict ready for the API.
### Attachments
```python theme={null}
import base64
with open('report.pdf', 'rb') as f:
encoded = base64.b64encode(f.read()).decode('utf-8')
attachment = {
'fileName': 'report.pdf',
'contentType': 'application/pdf',
'content': encoded
}
```
Pass attachments via the `optional_headers` dict:
```python theme={null}
optional_headers = {
'attachments': [attachment]
}
```
## Send a message
```python theme={null}
response = client.send(mail.get())
print('Status:', response.status_code)
print('Tracking ID:', response.to_dict.get('sourceTrackingId'))
```
## Check delivery status
```python theme={null}
tracking_id = response.to_dict['sourceTrackingId']
disposition = client.get(tracking_id)
deliveries = disposition.to_dict['data']['message']['message_deliveries']
for d in deliveries:
print(d['recipient'], '→', d['status']['deliveryStatus'])
```
Common `deliveryStatus` values: `delivered`, `opened`, `failed`, `pending`.
## The Response object
Both `send` and `get` return a `Response` object:
| Property | Description |
| -------------- | ------------------------------------------------ |
| `.status_code` | HTTP status code |
| `.headers` | Response headers dict |
| `.text` | Raw response body as a string |
| `.to_dict` | JSON-parsed response body, or `None` if not JSON |
## Error handling
Both methods raise `requests.exceptions.HTTPError` on non-2xx responses. Wrap calls in a try/except block:
```python theme={null}
import requests
try:
response = client.send(mail.get())
except requests.exceptions.HTTPError as e:
print('API error:', e.response.status_code, e.response.text)
```
The optional `handle_error` helper prints the error response body before re-raising:
```python theme={null}
from paubox.helpers.errors import handle_error
try:
response = client.send(mail.get())
except requests.exceptions.HTTPError as e:
handle_error(e) # prints body, then re-raises
```
# Forms client
Source: https://docs.paubox.com/python-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Python SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Instantiation
```python theme={null}
from paubox import PauboxFormsClient
client = PauboxFormsClient()
```
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```python theme={null}
response = client.get_form('your-form-uuid')
form = response.to_dict
print('Title:', form['title'])
print('HTML:', form['form_html'])
```
Key fields in the response:
| Field | Description |
| ------------------ | ----------------------------------------- |
| `title` | Display name of the form |
| `form_json` | Parsed field schema |
| `form_html` | Rendered HTML for embedding |
| `form_css` | Associated stylesheet |
| `active` | Whether the form is accepting submissions |
| `submission_count` | Number of submissions received |
## Submit a form
```python theme={null}
form_data = {
'first_name': 'Jane',
'last_name': 'Smith',
'email': 'jane@example.com'
}
response = client.submit_form('your-form-uuid', form_data)
print('Status:', response.status_code) # 201 on success
```
`submit_form` raises `ValueError` if `form_data` is empty or `None`.
### Submitting with file attachments
The maximum total request size is 250 MB.
```python theme={null}
import base64
with open('consent.pdf', 'rb') as f:
encoded = base64.b64encode(f.read()).decode('utf-8')
attachments = [
{'name': 'consent.pdf', 'content': encoded}
]
response = client.submit_form('your-form-uuid', form_data, attachments)
```
Each attachment is a dict with two keys:
| Key | Description |
| --------- | ----------------------------- |
| `name` | File name including extension |
| `content` | Base64-encoded file content |
## Error handling
```python theme={null}
import requests
try:
response = client.get_form('your-form-uuid')
response = client.submit_form('your-form-uuid', form_data)
except ValueError as e:
print('Invalid input:', e)
except requests.exceptions.HTTPError as e:
print('API error:', e.response.status_code, e.response.text)
```
# Paubox Python SDK
Source: https://docs.paubox.com/python-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Python 3 applications using the official Paubox Python SDK.
The Paubox Python SDK is the official Python 3 client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). It provides a clean interface for sending encrypted email, tracking delivery, and submitting form responses with a single external dependency (`requests`).
```bash theme={null}
pip3 install paubox-python3
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Add CC, BCC, attachments, and HTML or plain-text content
* Track delivery status and engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Python 3
* `requests` (installed automatically)
## Get started
Install, configure credentials, and send your first email in minutes.
Set up your API key and endpoint host via environment variables.
Full reference for composing and sending email, and checking delivery status.
Retrieve form schemas and submit responses.
## Version note
The current stable release (v1) covers sending email and the Forms API. A v2 beta on the `sdk-generation/v2.0.0-beta` branch adds bulk sending and dynamic templates. This documentation covers v1.
# Quickstart
Source: https://docs.paubox.com/python-sdk/quickstart
Install the Paubox Python SDK and send your first HIPAA compliant email in minutes.
```bash theme={null}
pip3 install paubox-python3
```
Export your API key and endpoint host as environment variables:
```bash theme={null}
export PAUBOX_API_KEY="YOUR_API_KEY"
export PAUBOX_HOST="https://api.paubox.com/v1"
```
To obtain your API key, see [Authentication](/python-sdk/authentication).
Create `send.py`:
```python theme={null}
from paubox import PauboxApiClient
from paubox.helpers.mail import Mail
client = PauboxApiClient()
mail = Mail(
from_ = 'sender@yourdomain.com',
subject = 'Your first Paubox email',
recipients = ['recipient@example.com'],
content = {'text/plain': 'This message was sent with the Paubox Python SDK.'}
)
response = client.send(mail.get())
print('Status:', response.status_code)
print('Response:', response.to_dict)
```
Run it:
```bash theme={null}
python3 send.py
```
The response dict contains `sourceTrackingId`.
```python theme={null}
tracking_id = response.to_dict['sourceTrackingId']
disposition = client.get(tracking_id)
print(disposition.to_dict)
```
The response includes `message_deliveries` with per-recipient status.
## Next steps
* [Email client](/python-sdk/email-client): add HTML, attachments, CC/BCC, and handle errors
* [Forms client](/python-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/rails-sdk/authentication
Configure your Paubox API credentials in a Rails initializer.
## Obtain your API key
The Email API requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Configure the initializer
Create `config/initializers/paubox.rb`:
```ruby theme={null}
Paubox.configure do |config|
config.api_key = ENV['PAUBOX_API_KEY']
end
```
The gem registers itself as a Railtie, so this initializer runs before ActionMailer is configured. Credentials are automatically applied to every email delivery; no per-request setup is needed.
## Set environment variables
Add your API key to your environment (e.g. via a `.env` file with `dotenv-rails`, Rails credentials, or your deployment platform's secret management):
```bash theme={null}
PAUBOX_API_KEY=YOUR_API_KEY
```
## Forms client
`PauboxRails::Forms::Client` requires no credentials. Form submissions are associated with the form ID, not an API key.
```ruby theme={null}
client = PauboxRails::Forms.client
```
## Security notes
* Never hard-code credentials in initializer files; always read from environment variables or encrypted credentials.
* The gem does not log API keys or request bodies.
# Email delivery
Source: https://docs.paubox.com/rails-sdk/email-delivery
Send HIPAA compliant email through ActionMailer using the Paubox Rails gem.
## How it works
`paubox_rails` registers `:paubox` as an ActionMailer delivery method. Your mailer classes are standard Rails mailers; the gem intercepts delivery and routes each message through the Paubox Email API, ensuring every email is encrypted and HIPAA compliant.
## Set the delivery method
**Globally** (all environments):
```ruby theme={null}
# config/application.rb
config.action_mailer.delivery_method = :paubox
```
**Per environment** (recommended for production only):
```ruby theme={null}
# config/environments/production.rb
config.action_mailer.delivery_method = :paubox
```
Leave development and test environments using `:letter_opener`, `:test`, or another local delivery method.
## Create a mailer
Mailers work exactly as documented in the [Rails ActionMailer guide](https://guides.rubyonrails.org/action_mailer_basics.html):
```ruby theme={null}
class PatientMailer < ApplicationMailer
def appointment_reminder
@patient = params[:patient]
@appointment = params[:appointment]
mail(
to: @patient.email,
from: "appointments@yourclinic.com",
subject: "Appointment reminder"
)
end
end
```
Templates live in `app/views/patient_mailer/` as usual (`appointment_reminder.html.erb`, `appointment_reminder.text.erb`).
## Deliver email
```ruby theme={null}
# Synchronous delivery
PatientMailer.with(patient: patient, appointment: appt).appointment_reminder.deliver_now
# Asynchronous delivery (via Active Job)
PatientMailer.with(patient: patient, appointment: appt).appointment_reminder.deliver_later
```
## Per-message options
Pass `delivery_method_options` in the `mail()` call to override delivery behavior for a specific message:
```ruby theme={null}
mail(
to: @patient.email,
subject: "Lab results",
delivery_method_options: { allow_non_tls: true }
)
```
## Attachments
Attach files using standard ActionMailer attachment helpers:
```ruby theme={null}
def report_email
attachments["results.pdf"] = File.read(Rails.root.join("tmp/results.pdf"))
mail(
to: params[:patient].email,
subject: "Your lab results"
)
end
```
## Tracking IDs
The ActionMailer delivery flow does not surface per-send tracking IDs. If you need to record and look up a tracking ID for a specific send, use the [Paubox Email API](/email-api) directly via a REST client alongside this gem.
# Forms client
Source: https://docs.paubox.com/rails-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Rails SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Create a client
```ruby theme={null}
client = PauboxRails::Forms.client
```
`PauboxRails::Forms.client` returns a new `PauboxRails::Forms::Client` instance.
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```ruby theme={null}
form = client.get_form("your-form-uuid")
puts form["title"]
puts form["form_html"]
puts form["form_json"].inspect
```
The return value is a Hash with the following keys:
| Key | Description |
| -------------------- | ----------------------------------------- |
| `"title"` | Display name of the form |
| `"form_json"` | Parsed field schema |
| `"form_html"` | Rendered HTML for embedding |
| `"form_css"` | Associated stylesheet |
| `"active"` | Whether the form is accepting submissions |
| `"submission_count"` | Number of submissions received |
`get_form` raises `PauboxRails::Forms::NotFoundError` if the form UUID is invalid.
## Submit a form
```ruby theme={null}
result = client.submit_form(
"your-form-uuid",
form_data: {
"first_name" => "Jane",
"last_name" => "Smith",
"email" => "jane@example.com"
}
)
# result => true
```
`submit_form` returns `true` on a successful submission (HTTP 201).
### Submitting with file attachments
The maximum total request size is 250 MB.
```ruby theme={null}
require "base64"
encoded = Base64.strict_encode64(File.read(Rails.root.join("tmp/consent.pdf")))
result = client.submit_form(
"your-form-uuid",
form_data: { "first_name" => "Jane" },
attachments: [
{ name: "consent.pdf", content: encoded }
]
)
```
Each attachment is a Hash with two keys:
| Key | Description |
| ---------- | ----------------------------- |
| `:name` | File name including extension |
| `:content` | Base64-encoded file content |
## Error handling
```ruby theme={null}
begin
form = client.get_form("your-form-uuid")
result = client.submit_form("your-form-uuid", form_data: data)
rescue PauboxRails::Forms::NotFoundError
# Form UUID not found (HTTP 404)
rescue PauboxRails::Forms::BadRequestError
# Malformed request (HTTP 400)
rescue PauboxRails::Forms::Error => e
# Any other Forms API error
Rails.logger.error("Forms error: #{e.message}")
end
```
# Paubox Rails SDK
Source: https://docs.paubox.com/rails-sdk/index
Send HIPAA compliant email from Rails applications using ActionMailer and the official Paubox Rails gem.
The Paubox Rails SDK (`paubox_rails`) integrates the [Paubox Email API](/email-api) directly into Rails as an ActionMailer delivery method. Your mailer classes work exactly as they do today; just point ActionMailer at Paubox and every email you send becomes encrypted and HIPAA compliant. The gem also includes a lightweight client for the [Paubox Forms API](/forms).
```ruby theme={null}
# Gemfile
gem 'paubox_rails'
```
## What you can do
* Send encrypted, HIPAA compliant email through standard Rails mailers and ActionMailer
* Use HTML and plain-text templates, attachments, and all standard ActionMailer features
* Deliver synchronously with `deliver_now` or asynchronously with `deliver_later`
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Rails 4 or later
* Ruby
* Dependencies installed automatically: `actionmailer` and the `paubox` gem
## Get started
Add the gem, configure credentials, and send your first email in minutes.
Configure your API key via a Rails initializer.
Full guide to sending email through ActionMailer with Paubox.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/rails-sdk/quickstart
Add the Paubox Rails gem, configure credentials, and send your first HIPAA compliant email in minutes.
Add `paubox_rails` to your Gemfile:
```ruby theme={null}
gem 'paubox_rails'
```
Then install:
```bash theme={null}
bundle install
```
Create `config/initializers/paubox.rb`:
```ruby theme={null}
Paubox.configure do |config|
config.api_key = ENV['PAUBOX_API_KEY']
end
```
To obtain credentials, see [Authentication](/rails-sdk/authentication).
In `config/application.rb` (or an environment-specific file):
```ruby theme={null}
config.action_mailer.delivery_method = :paubox
```
Generate a mailer if you don't have one:
```bash theme={null}
rails generate mailer UserMailer
```
Define an action in `app/mailers/user_mailer.rb`:
```ruby theme={null}
class UserMailer < ApplicationMailer
def welcome_email
@user = params[:user]
mail(
to: @user.email,
from: "noreply@yourdomain.com",
subject: "Welcome to the portal"
)
end
end
```
Add a template at `app/views/user_mailer/welcome_email.html.erb`:
```erb theme={null}
Hello <%= @user.name %>,
Your account is ready. Log in at any time.
```
Deliver it:
```ruby theme={null}
UserMailer.with(user: current_user).welcome_email.deliver_now
```
The email is sent encrypted via Paubox.
## Next steps
* [Email delivery](/rails-sdk/email-delivery): per-message options, attachments, async delivery
* [Forms client](/rails-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/ruby-sdk/authentication
Configure your Paubox API credentials for the Ruby SDK.
## Obtain your credentials
The Email API client requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Global configuration (recommended)
Set your API key once at application startup. All `Paubox::Client` instances will use it automatically:
```ruby theme={null}
require 'paubox'
Paubox.configure do |config|
config.api_key = ENV['PAUBOX_API_KEY']
end
client = Paubox::Client.new
```
## Per-client configuration
Override the API key for a specific client instance:
```ruby theme={null}
client = Paubox::Client.new(api_key: 'YOUR_API_KEY')
```
The SDK sets the `Authorization` header automatically on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## Forms client
`Paubox::FormsClient` requires no credentials:
```ruby theme={null}
forms_client = Paubox::FormsClient.new
```
## Security notes
* Never hard-code credentials in source files; always read from environment variables.
* The SDK does not log API keys or request bodies.
# Email client
Source: https://docs.paubox.com/ruby-sdk/email-client
Full reference for the Paubox Ruby SDK email client: building messages, sending email, tracking delivery, dynamic templates, and error handling.
## The client
```ruby theme={null}
client = Paubox::Client.new # uses global Paubox.configure credentials
client = Paubox::Client.new(api_key: 'YOUR_API_KEY') # or a per-client API key
```
`send_mail` and `deliver_mail` are aliases. `email_disposition` and `message_receipt` are aliases.
## Building a message
```ruby theme={null}
message = Paubox::Message.new(
from: 'sender@yourdomain.com', # required
to: ['alice@example.com'], # required; array
subject: 'Your results are ready', # optional
reply_to: 'support@yourdomain.com', # optional
cc: ['manager@example.com'], # optional
bcc: ['audit@example.com'], # optional
text_content: 'Plain text body.', # at least one required
html_content: 'HTML body.
', # optional
allow_non_tls: false, # optional; default false
force_secure_notification: false # optional; default false
)
```
### Attachments
Attach files by path using the helper method:
```ruby theme={null}
message.add_attachment('/path/to/report.pdf')
```
Or set the `attachments` attribute directly with an array of hashes (each with `fileName`, `contentType`, and base64-encoded `content`):
```ruby theme={null}
require 'base64'
message.attachments = [
{
fileName: 'report.pdf',
contentType: 'application/pdf',
content: Base64.strict_encode64(File.read('/path/to/report.pdf'))
}
]
```
## Send a message
```ruby theme={null}
response = client.send_mail(message)
puts response[:source_tracking_id]
```
## Check delivery status
```ruby theme={null}
disposition = client.email_disposition(source_tracking_id)
disposition.message_deliveries.each do |delivery|
puts "#{delivery.recipient} → #{delivery.status.delivery_status}"
puts " Opened: #{delivery.status.opened_status}"
end
```
`EmailDisposition` fields:
| Field | Description |
| -------------------- | ---------------------------------- |
| `source_tracking_id` | The tracking ID |
| `message_deliveries` | Array of `MessageDelivery` structs |
| `errors` | Array of `ResponseError` structs |
Each `MessageDelivery` struct has `recipient` and `status`. The `status` struct has `delivery_status`, `delivery_time`, `opened_status`, and `opened_time`.
Check `disposition.errors?` to detect API-level errors.
Common `delivery_status` values: `delivered`, `opened`, `failed`, `pending`.
## Dynamic templates
Templates use [Handlebars](https://handlebarsjs.com) syntax (`{{variable_name}}`).
### Create a template
```ruby theme={null}
Paubox::DynamicTemplates.create('appointment-confirmation', '/path/to/template.html')
```
### List templates
```ruby theme={null}
templates = Paubox::DynamicTemplates.list
templates.each { |t| puts "#{t[:id]}: #{t[:name]}" }
```
### Get a template
```ruby theme={null}
template = Paubox::DynamicTemplates.find(template_id)
```
### Update a template
```ruby theme={null}
template.update('/path/to/updated-template.html')
# or rename:
template.update('/path/to/updated-template.html', 'new-template-name')
```
### Delete a template
```ruby theme={null}
template.delete
```
### Send a templated message
```ruby theme={null}
message = Paubox::TemplatedMessage.new(
from: 'appointments@yourclinic.com',
to: ['jane@example.com'],
subject: 'Your appointment is confirmed',
template: {
name: 'appointment-confirmation',
values: {
first_name: 'Jane',
date: '2024-03-15',
time: '2:00 PM'
}
}
)
response = client.send_mail(message)
```
## Error handling
Methods raise `RestClient::ExceptionWithResponse` on HTTP errors. Rescue it to inspect the response:
```ruby theme={null}
require 'rest-client'
begin
response = client.send_mail(message)
rescue RestClient::ExceptionWithResponse => e
puts "HTTP #{e.response.code}: #{e.response.body}"
end
```
# Forms client
Source: https://docs.paubox.com/ruby-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Ruby SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Instantiation
```ruby theme={null}
require 'paubox'
forms_client = Paubox::FormsClient.new
```
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```ruby theme={null}
form = forms_client.get_form('your-form-uuid')
puts form.title
puts form.form_html
```
The returned `Paubox::Form` object exposes:
| Method / Attribute | Description |
| -------------------------- | ----------------------------------------- |
| `#title` | Display name of the form |
| `#form_json` | Parsed field schema |
| `#form_html` | Rendered HTML for embedding |
| `#form_css` | Associated stylesheet |
| `#active?` | Whether the form is accepting submissions |
| `#submission_count` | Number of submissions received |
| `#signable?` | Whether the form supports e-signatures |
| `#deleted?` / `#archived?` | Form state flags |
## Submit a form
```ruby theme={null}
form_data = {
first_name: 'Jane',
last_name: 'Smith',
email: 'jane@example.com'
}
response = forms_client.submit_form('your-form-uuid', form_data: form_data)
# response is 201 Created on success
```
### Submitting with file attachments
The maximum total request size is 250 MB.
```ruby theme={null}
require 'base64'
encoded = Base64.strict_encode64(File.read('consent.pdf'))
attachments = [
{ name: 'consent.pdf', content: encoded }
]
response = forms_client.submit_form(
'your-form-uuid',
form_data: form_data,
attachments: attachments
)
```
## Error handling
```ruby theme={null}
require 'rest-client'
begin
form = forms_client.get_form('your-form-uuid')
response = forms_client.submit_form('your-form-uuid', form_data: form_data)
rescue RestClient::ExceptionWithResponse => e
puts "HTTP #{e.response.code}: #{e.response.body}"
end
```
# Paubox Ruby SDK
Source: https://docs.paubox.com/ruby-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Ruby applications using the official Paubox Ruby gem.
The Paubox Ruby SDK (`paubox` gem) is the core Ruby client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). It provides direct API access for sending encrypted email, managing dynamic templates, tracking delivery with typed response objects, and submitting form responses.
```ruby theme={null}
# Gemfile
gem 'paubox'
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Create, update, and delete dynamic Handlebars templates
* Send templated messages with per-message variable substitution
* Track delivery status and open engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Ruby >= 2.3
* Dependencies installed automatically: `mail` and `rest-client`
## Rails applications
If you are using Rails, the [`paubox_rails`](/rails-sdk) gem builds on top of this library and adds ActionMailer integration. Use `paubox` directly for non-Rails Ruby applications, scripts, or background jobs.
## Get started
Configure credentials and send your first email in minutes.
Configure globally or per-client with your API key.
Full reference for sending email, managing templates, and tracking delivery.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/ruby-sdk/quickstart
Install the Paubox Ruby gem and send your first HIPAA compliant email in minutes.
Add to your Gemfile:
```ruby theme={null}
gem 'paubox'
```
Then install:
```bash theme={null}
bundle install
```
Or install directly:
```bash theme={null}
gem install paubox
```
```ruby theme={null}
require 'paubox'
Paubox.configure do |config|
config.api_key = ENV['PAUBOX_API_KEY']
end
```
To obtain your API key, see [Authentication](/ruby-sdk/authentication).
```ruby theme={null}
client = Paubox::Client.new
message = Paubox::Message.new(
from: 'sender@yourdomain.com',
to: ['recipient@example.com'],
subject: 'Your first Paubox email',
text_content: 'This message was sent with the Paubox Ruby SDK.'
)
response = client.send_mail(message)
puts "Sent. Tracking ID: #{response[:source_tracking_id]}"
```
```ruby theme={null}
tracking_id = response[:source_tracking_id]
disposition = client.email_disposition(tracking_id)
disposition.message_deliveries.each do |delivery|
puts "#{delivery.recipient} → #{delivery.status.delivery_status}"
end
```
## Next steps
* [Email client](/ruby-sdk/email-client): attachments, dynamic templates, error handling
* [Forms client](/ruby-sdk/forms-client): retrieve form schemas and submit responses
# Authentication
Source: https://docs.paubox.com/rust-sdk/authentication
Configure your Paubox API credentials for the Rust SDK.
## Obtain your credentials
The Email API client requires one credential:
* **API key**: a secret token that authenticates your requests
It is available in the [Paubox dashboard](https://next.paubox.com) under **API Credentials**.
## Option 1: Environment variables (recommended)
```bash theme={null}
export PAUBOX_API_KEY="YOUR_API_KEY"
```
```rust theme={null}
let client = PauboxClient::from_env()?;
```
`from_env()` returns `Err(PauboxError::EnvVar(...))` immediately if the variable is missing or empty.
## Option 2: Direct constructor
```rust theme={null}
let client = PauboxClient::new("YOUR_API_KEY");
```
## Option 3: Builder (with custom timeout or base URL)
```rust theme={null}
use std::time::Duration;
let client = PauboxClient::builder()
.api_key("YOUR_API_KEY")
.timeout(Duration::from_secs(15))
.build()?;
```
Use `.base_url(url)` in tests to point at a mock server.
The SDK sets the `Authorization` header automatically on every request:
```
Authorization: Token token=YOUR_API_KEY
```
## Forms client
`FormsClient` requires no credentials:
```rust theme={null}
let forms = FormsClient::new();
```
If you already have a `PauboxClient`, reuse its HTTP connection pool:
```rust theme={null}
let forms = email_client.forms();
```
## Security notes
* Never hard-code credentials in source files.
* The SDK does not log API keys or request bodies.
# Email client
Source: https://docs.paubox.com/rust-sdk/email-client
Full reference for the Paubox Rust SDK email client: building messages, sending email, tracking delivery, and error handling.
## Creating a client
```rust theme={null}
// From environment variables (recommended)
let client = PauboxClient::from_env()?;
// Direct constructor
let client = PauboxClient::new("YOUR_API_KEY");
// Builder (custom timeout, base URL for testing)
let client = PauboxClient::builder()
.api_key("YOUR_API_KEY")
.timeout(Duration::from_secs(15))
.build()?;
```
See [Authentication](/rust-sdk/authentication) for details on all three options.
## Building a message
Use `Message::builder()` to compose a message. All methods take `impl Into` or iterables:
```rust theme={null}
use paubox::Message;
let message = Message::builder()
.from("sender@yourdomain.com") // required
.to(["alice@example.com"]) // required; accepts any iterable
.subject("Your results are ready") // required
.text_content("Plain text body.") // at least one of text/html required
.html_content("HTML body.
") // optional
.reply_to("support@yourdomain.com") // optional
.cc(["manager@example.com"]) // optional
.bcc(["audit@example.com"]) // optional
.allow_non_tls(false) // optional; default false
.force_secure_notification(true) // optional
.build()?;
```
`.build()` validates required fields and returns `Err(PauboxError::Validation(...))` if any are missing.
### Attachments
The `from_bytes` constructor base64-encodes the data automatically; no manual encoding needed:
```rust theme={null}
use paubox::Attachment;
use std::fs;
let data = fs::read("report.pdf")?;
let attachment = Attachment::from_bytes("report.pdf", "application/pdf", &data);
```
If you already have base64-encoded content:
```rust theme={null}
let attachment = Attachment::from_base64("report.pdf", "application/pdf", encoded_string);
```
Add attachments to a message via the builder:
```rust theme={null}
let message = Message::builder()
// ...other fields...
.attachment(attachment)
.build()?;
```
## Send a message
```rust theme={null}
let response = client.send_message(&message).await?;
println!("Tracking ID: {}", response.source_tracking_id);
```
`SendResponse` fields:
| Field | Description |
| -------------------- | ---------------------------- |
| `source_tracking_id` | Use to check delivery status |
| `message` | Status message from the API |
## Check delivery status
```rust theme={null}
let disposition = client
.get_email_disposition(&response.source_tracking_id)
.await?;
for delivery in &disposition.message_deliveries {
println!(
"{} → {} (opened: {})",
delivery.recipient,
delivery.delivery_status,
delivery.opened_status
);
}
```
`MessageDelivery` fields:
| Field | Description |
| ----------------- | ------------------------------------- |
| `recipient` | Email address |
| `delivery_status` | e.g. `delivered`, `failed`, `pending` |
| `delivery_time` | ISO 8601 timestamp, if delivered |
| `opened_status` | `opened` or `unopened` |
| `opened_time` | ISO 8601 timestamp, if opened |
## Health check
```rust theme={null}
client.api_status().await?;
```
Returns `Ok(())` if the API is reachable, or a `PauboxError` if not.
## Error handling
All methods return `Result`. Match on the variants to handle specific conditions:
```rust theme={null}
use paubox::PauboxError;
match client.send_message(&message).await {
Ok(response) => println!("Sent: {}", response.source_tracking_id),
Err(PauboxError::Auth(msg)) => eprintln!("Invalid credentials: {}", msg),
Err(PauboxError::Http { status, body }) => {
eprintln!("HTTP {}: {}", status, body);
}
Err(PauboxError::Validation(msg)) => eprintln!("Invalid message: {}", msg),
Err(e) => eprintln!("Error: {}", e),
}
```
`PauboxError` variants:
| Variant | When it occurs |
| -------------------------------- | -------------------------------------------- |
| `Auth(String)` | HTTP 401: invalid or missing credentials |
| `Http { status, body }` | Non-2xx response other than 401 |
| `Request(reqwest::Error)` | Network or TLS failure |
| `Deserialize(serde_json::Error)` | Unexpected response format |
| `Validation(String)` | Missing required message field |
| `EnvVar(String)` | Missing environment variable in `from_env()` |
| `Url(url::ParseError)` | URL construction failure |
# Forms client
Source: https://docs.paubox.com/rust-sdk/forms-client
Retrieve Paubox Form schemas and submit form responses using the Rust SDK.
The Forms client does not require API credentials. Forms are identified by a UUID that you obtain from the Paubox dashboard.
## Creating a client
**Standalone** (creates its own HTTP connection pool):
```rust theme={null}
use paubox::FormsClient;
let client = FormsClient::new();
```
**Shared connection pool** (reuses the HTTP client from an existing `PauboxClient`):
```rust theme={null}
let forms = email_client.forms();
```
## Get a form
Retrieve a form's metadata, field schema, and rendered HTML/CSS:
```rust theme={null}
let form = client.get_form("your-form-uuid").await?;
println!("Title: {}", form.title);
if let Some(html) = &form.form_html {
println!("HTML: {}", html);
}
```
`Form` fields:
| Field | Type | Description |
| ------------------ | --------------------------- | ----------------------------------------- |
| `title` | `String` | Display name of the form |
| `form_json` | `Option` | Parsed field schema |
| `form_html` | `Option` | Rendered HTML for embedding |
| `form_css` | `Option` | Associated stylesheet |
| `active` | `bool` | Whether the form is accepting submissions |
| `submission_count` | `u64` | Number of submissions received |
## Submit a form
Build a `FormSubmission` and call `submit_form`:
```rust theme={null}
use paubox::FormSubmission;
use serde_json::json;
let submission = FormSubmission::builder()
.form_data(json!({
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com"
}))
.build()?;
client.submit_form("your-form-uuid", &submission).await?;
```
`submit_form` returns `Ok(())` on success (HTTP 201).
### Submitting with file attachments
The maximum total request size is 250 MB. `FormAttachment::from_bytes` base64-encodes automatically:
```rust theme={null}
use paubox::{FormAttachment, FormSubmission};
use std::fs;
let data = fs::read("consent.pdf")?;
let attachment = FormAttachment::from_bytes("consent.pdf", &data);
let submission = FormSubmission::builder()
.form_data(json!({ "first_name": "Jane" }))
.attachment(attachment)
.build()?;
client.submit_form("your-form-uuid", &submission).await?;
```
If you already have base64-encoded content:
```rust theme={null}
let attachment = FormAttachment::from_base64("consent.pdf", encoded_string);
```
## Error handling
```rust theme={null}
use paubox::PauboxError;
match client.get_form("your-form-uuid").await {
Ok(form) => println!("Form: {}", form.title),
Err(PauboxError::Http { status, body }) => {
eprintln!("HTTP {}: {}", status, body);
}
Err(e) => eprintln!("Error: {}", e),
}
```
# Paubox Rust SDK
Source: https://docs.paubox.com/rust-sdk/index
Send HIPAA compliant email and submit Paubox Forms from Rust applications using the official Paubox Rust SDK.
The Paubox Rust SDK is the official async Rust client library for the [Paubox Email API](/email-api) and [Paubox Forms](/forms). All methods are `async` and return `Result`, making them composable with any Tokio-based application.
```toml theme={null}
# Cargo.toml
[dependencies]
paubox = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
## What you can do
* Send encrypted, HIPAA compliant email to one or more recipients
* Add CC, BCC, attachments, and HTML or plain-text content
* Track delivery status and open engagement per recipient
* Retrieve Paubox Form schemas and submit form responses with file attachments
## Requirements
* Rust 1.75 or later
* Tokio async runtime
## Feature flags
Both features are enabled by default. Disable the ones you don't need to reduce compile times:
```toml theme={null}
# Email API only
paubox = { version = "0.1", default-features = false, features = ["email"] }
# Forms API only
paubox = { version = "0.1", default-features = false, features = ["forms"] }
```
| Feature | What it includes |
| ------- | ------------------------------------------------------------------------------ |
| `email` | `PauboxClient`, `Message`, `Attachment`, `SendResponse`, `DispositionResponse` |
| `forms` | `FormsClient`, `Form`, `FormSubmission`, `FormAttachment` |
## Get started
Configure credentials and send your first email in minutes.
Three ways to supply your API key.
Full reference for building messages, sending email, and tracking delivery.
Retrieve form schemas and submit responses.
# Quickstart
Source: https://docs.paubox.com/rust-sdk/quickstart
Install the Paubox Rust SDK and send your first HIPAA compliant email in minutes.
Add `paubox` and `tokio` to your `Cargo.toml`:
```toml theme={null}
[dependencies]
paubox = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```
Export your API key as an environment variable:
```bash theme={null}
export PAUBOX_API_KEY="YOUR_API_KEY"
```
To obtain your API key, see [Authentication](/rust-sdk/authentication).
Create `src/main.rs`:
```rust theme={null}
use paubox::{Message, PauboxClient};
#[tokio::main]
async fn main() -> Result<(), Box> {
let client = PauboxClient::from_env()?;
let message = Message::builder()
.from("sender@yourdomain.com")
.to(["recipient@example.com"])
.subject("Your first Paubox email")
.text_content("This message was sent with the Paubox Rust SDK.")
.build()?;
let response = client.send_message(&message).await?;
println!("Sent. Tracking ID: {}", response.source_tracking_id);
Ok(())
}
```
Run it:
```bash theme={null}
cargo run
```
```
Sent. Tracking ID: abc123-def456-...
```
```rust theme={null}
let disposition = client
.get_email_disposition(&response.source_tracking_id)
.await?;
for delivery in &disposition.message_deliveries {
println!("{} → {}", delivery.recipient, delivery.delivery_status);
}
```
```
recipient@example.com → delivered
```
## Next steps
* [Email client](/rust-sdk/email-client): attachments, CC/BCC, error handling
* [Forms client](/rust-sdk/forms-client): retrieve form schemas and submit responses
# SDKs
Source: https://docs.paubox.com/sdks
Official client libraries for the Paubox Email API and Forms API.
Paubox provides official SDKs for nine languages. Each library handles authentication, request formatting, and error handling so you can integrate in minutes.
Source code and issue trackers for all SDKs are available on [GitHub](https://github.com/paubox).
# Introduction
Source: https://docs.paubox.com/welcome
Paubox is HIPAA compliant, HITRUST certified, email infrastructure. Send transactional email, run marketing campaigns, and collect patient data via forms, all without worrying about PHI exposure.
## Which product is right for me?
| I want to… | Use… |
| :---------------------------------------------- | :----------------------------------- |
| Send transactional email from my app | [Email API](/email-api/quickstart) |
| Run email campaigns to patient lists | [Marketing API](/marketing) |
| Collect patient intake or consent data | [Forms API](/forms/index) |
| Send from the terminal or CI pipeline | [CLI](/cli/quickstart) |
| Use Paubox from Claude or AI agents | [MCP Server](/mcp-server/quickstart) |
| Use Paubox from my app (Go, Node, Python, etc.) | [SDKs](/sdks) |
## Products
Send transactional, HIPAA compliant email via REST or SMTP
Campaigns, drip sequences, subscribers, and analytics
Collect patient data via HIPAA compliant secure forms
Send email and check delivery from the terminal or CI
Give AI assistants access to Paubox Email and Forms tools
## Get started
Four steps to your first email
Auth, formatting, and response codes
Send email via HTTP
Send email via SMTP
## Quickstart by language
## Building an SDK or integration?
Start with our OpenAPI specs: point your generator, AI agent, or editor at them directly.
Transactional email: `api.paubox.com/v1/email`
Campaigns, subscribers, analytics: `api.paubox.com/v1/marketing`
## Need help?
SDK source and issues
System uptime
Contact the team
Have a question, an idea, or something you built to show off? Join the [Paubox Community](https://github.com/Paubox/community/discussions), the single home for discussions across every Paubox SDK and API (Email API, Forms, Marketing, and the MCP/CLI tooling). Never post PHI, recipient addresses, or message content in public threads. Anything sensitive goes to [support@paubox.com](mailto:support@paubox.com).