Skip to content
Pidgely Docs

API reference · Emails

Send an email

POSThttps://api.pidgely.com/emails
View as Markdown

Queues one email for delivery and returns its id. The from domain must be verified and allowed for the API key. Up to 50 unique recipients across to, cc and bcc; a text part is generated from html when you omit text.

scheduled_at accepts ISO 8601 or natural language such as in 1 hour or tomorrow at 9am (UTC), up to 30 days ahead. Attachments take base64 content or a remote path. Send an Idempotency-Key to retry safely. See Sending email for every option.

Authorization: Bearer pdg_…. Any API key (full_access or sending_access).

Headers

Name Type Required Description
Idempotency-Key string No Replays the stored response for 24 hours; a different body gets 409. Up to 256 characters.

Body parameters

JSON object.

Name Type Required Description
from string No Name <addr@verified-domain>; may come from the template.
to string | string[] Yes Recipient address (a@x.com or Name <a@x.com>) or a list of them. Up to 50 unique recipients across to, cc and bcc.
cc string | string[] No Carbon-copy recipients. Count toward the 50-recipient limit.
bcc string | string[] No Blind carbon-copy recipients. Count toward the 50-recipient limit.
reply_to string | string[] No Reply-To address or list of addresses.
subject string No Subject line, up to 998 bytes. Required unless a template provides it.
html string No HTML body. html or text is required (unless you send with a template).
text string No Generated from html when omitted.
headers object (map of string) No Custom headers. Platform-managed headers (From, To, Subject, Message-ID, DKIM-Signature, Return-Path, Reply-To, …) are rejected; values cannot contain line breaks.
attachments object[] No Files to attach. All attachments share a 40 MiB budget (decoded). Executable types (.exe, .js, .bat, …) are blocked.
attachments[].filename string No File name shown to the recipient. Defaults to the last segment of path.
attachments[].content string No Base64 content. Use either content or path.
attachments[].path string No http(s) URL downloaded at send time (SSRF-guarded, 40MB budget, no redirects).
attachments[].content_type string No MIME type. Guessed from the file extension when omitted.
attachments[].content_id string No Makes the attachment inline (cid: in HTML).
tags object[] No Name/value pairs attached to the email and returned in webhooks.
tags[].name string Yes Display name. Pattern: ^[A-Za-z0-9_-]{1,256}$.
tags[].value string Yes Pattern: ^[A-Za-z0-9_-]{1,256}$.
scheduled_at string No ISO 8601 or natural language (in 1 hour, tomorrow at 9am); within 30 days.
template object No —
template.id string Yes Template id or alias (published version is used).
template.variables object (map of string | number) No Values for the template variables: strings up to 2,000 characters or numbers.
topic_id string No Not supported yet; a non-empty value is rejected.
stream string No Pidgely extension: transactional (default) or broadcast (adds RFC 8058 unsubscribe). One of transactional, broadcast.

Request

Node.js
import { Pidgely } from 'pidgely';

const pidgely = new Pidgely(process.env.PIDGELY_API_KEY);

const { data, error } = await pidgely.emails.send({
  from: 'Acme <onboarding@acme.com>',
  to: ['delivered@example.com'],
  subject: 'Welcome to Acme',
  html: '<p>Thanks for signing up!</p>',
  tags: [
    {
      name: 'category',
      value: 'welcome',
    },
  ],
}, {
  idempotencyKey: 'welcome-user-123',
});
console.log(data ?? error);
Python
import os
import pidgely

pidgely.api_key = os.environ["PIDGELY_API_KEY"]

result = pidgely.Emails.send({
    "from": "Acme <onboarding@acme.com>",
    "to": ["delivered@example.com"],
    "subject": "Welcome to Acme",
    "html": "<p>Thanks for signing up!</p>",
    "tags": [
        {
            "name": "category",
            "value": "welcome",
        },
    ],
}, {
    "idempotency_key": "welcome-user-123",
})
print(result)
PHP
<?php

require __DIR__ . '/vendor/autoload.php';

$pidgely = Pidgely::client(getenv('PIDGELY_API_KEY'));

$result = $pidgely->emails->send([
    'from' => 'Acme <onboarding@acme.com>',
    'to' => ['delivered@example.com'],
    'subject' => 'Welcome to Acme',
    'html' => '<p>Thanks for signing up!</p>',
    'tags' => [
        [
            'name' => 'category',
            'value' => 'welcome',
        ],
    ],
], [
    'idempotency_key' => 'welcome-user-123',
]);
var_dump($result);
Go
package main

import (
	"context"
	"fmt"
	"os"

	"github.com/pidgely/pidgely-go"
)

func main() {
	ctx := context.Background()
	client := pidgely.NewClient(os.Getenv("PIDGELY_API_KEY"))

	result, err := client.Emails.Send(ctx, &pidgely.SendEmailRequest{
		From:    "Acme <onboarding@acme.com>",
		To:      []string{"delivered@example.com"},
		Subject: "Welcome to Acme",
		Html:    "<p>Thanks for signing up!</p>",
		Tags:    []pidgely.Tag{
		{
			Name:  "category",
			Value: "welcome",
		},
	},
	}, pidgely.WithIdempotencyKey("welcome-user-123"))
	if err != nil {
		panic(err)
	}
	fmt.Println(result)
}
Ruby
require "pidgely"

Pidgely.api_key = ENV["PIDGELY_API_KEY"]

result = Pidgely::Emails.send({
  from: "Acme <onboarding@acme.com>",
  to: ["delivered@example.com"],
  subject: "Welcome to Acme",
  html: "<p>Thanks for signing up!</p>",
  tags: [
    {
      name: "category",
      value: "welcome"
    }
  ]
}, {
  idempotency_key: "welcome-user-123"
})
puts result
cURL
curl -X POST 'https://api.pidgely.com/emails' \
  -H "Authorization: Bearer $PIDGELY_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: welcome-user-123' \
  -d '{
  "from": "Acme <onboarding@acme.com>",
  "to": [
    "delivered@example.com"
  ],
  "subject": "Welcome to Acme",
  "html": "<p>Thanks for signing up!</p>",
  "tags": [
    {
      "name": "category",
      "value": "welcome"
    }
  ]
}'

Response

200 OK

JSON
{
  "id": "0199d8a2-6f3c-7c51-9e2a-3b1f0c6d4e21"
}

Response fields

Name Type Required Description
id string Yes Unique id (UUID). UUID.

Errors

Errors have the shape {"statusCode", "name", "message"}. See Errors for the full list.

Status Name When
401 missing_api_key, restricted_api_key No Authorization: Bearer header, or a sending_access key called an endpoint that needs full_access.
403 invalid_api_key, validation_error The API key is invalid or revoked, or the request is not allowed for this account (unverified domain, plan limit, paused account).
404 not_found The resource does not exist in this account (or the id is not a valid UUID).
422 validation_error A field is missing or invalid; message names it.
429 rate_limit_exceeded Too many requests per second. Wait for Retry-After seconds.
403 validation_error The from domain is not verified, the key is restricted to another domain, or sending is paused for the account.
409 invalid_idempotent_request The Idempotency-Key was already used with a different request body.
400 invalid_idempotency_key The Idempotency-Key is longer than 256 characters.
422 invalid_attachment An attachment has neither content nor path.
429 daily_quota_exceeded The daily sending quota is used up (UTC day).
429 monthly_quota_exceeded The monthly sending quota is used up.