DigitalPigeon

API

Send email over HTTP or SMTP. The request and response shapes match Resend's, so code written against them — including code an AI assistant wrote for you — works here after changing the base URL and the key.

Before you can send

Two things, once per domain:

  1. Add and verify the domain. We give you three DNS records and check for them ourselves.
  2. Create an API key. Shown once. We store only a hash, so it cannot be shown again.

You can only send from a domain you have verified. This is why the service cannot be used to send as someone else, and it is not optional.

Use a subdomain like mail.yourcompany.com, and a separate one such as news.yourcompany.com for newsletters — complaints about a newsletter should never be able to hurt delivery of your password resets.

Authentication

Your key goes in the Authorization header on every request.

Header
Authorization: Bearer dp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

The same key is the password for SMTP. Keep it server-side: anyone holding it can send as your verified domains.

Send an email

POST https://digitalpigeon.golux.tech/v1/emails. The response comes back as soon as the message is queued, so a slow recipient never slows your request.

curl
curl -X POST https://digitalpigeon.golux.tech/v1/emails \
  -H "Authorization: Bearer dp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Prodavnica <hello@mail.prodavnica.rs>",
    "to": ["kupac@example.com"],
    "subject": "Narudžbina #4471 je potvrđena",
    "html": "<p>Hvala na narudžbini.</p>",
    "text": "Hvala na narudžbini."
  }'
Node
const response = await fetch("https://digitalpigeon.golux.tech/v1/emails", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.DIGITALPIGEON_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    from: "Prodavnica <hello@mail.prodavnica.rs>",
    to: ["kupac@example.com"],
    subject: "Narudžbina #4471 je potvrđena",
    html: "<p>Hvala na narudžbini.</p>",
    text: "Hvala na narudžbini.",
  }),
});

const { id } = await response.json();
Ruby
require "net/http"
require "json"

uri = URI("https://digitalpigeon.golux.tech/v1/emails")
request = Net::HTTP::Post.new(uri, {
  "Authorization" => "Bearer #{ENV.fetch('DIGITALPIGEON_API_KEY')}",
  "Content-Type" => "application/json"
})
request.body = {
  from: "Prodavnica <hello@mail.prodavnica.rs>",
  to: ["kupac@example.com"],
  subject: "Narudžbina #4471 je potvrđena",
  html: "<p>Hvala na narudžbini.</p>",
  text: "Hvala na narudžbini."
}.to_json

response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
id = JSON.parse(response.body)["id"]
Python
import os, requests

response = requests.post(
    "https://digitalpigeon.golux.tech/v1/emails",
    headers={"Authorization": f"Bearer {os.environ['DIGITALPIGEON_API_KEY']}"},
    json={
        "from": "Prodavnica <hello@mail.prodavnica.rs>",
        "to": ["kupac@example.com"],
        "subject": "Narudžbina #4471 je potvrđena",
        "html": "<p>Hvala na narudžbini.</p>",
        "text": "Hvala na narudžbini.",
    },
)

message_id = response.json()["id"]
Response
{ "id": "01a06673-4d4b-7173-a296-487a2e8917b1" }

Send text alongside html. Filters weight HTML-only mail as more likely to be bulk, and some clients render nothing without it.

Not sending twice

Add an Idempotency-Key header. A repeated request with the same key returns the original message id instead of sending again, for 24 hours.

Header
Idempotency-Key: order-4471-confirmation

Use something derived from what you are sending about, not a random value — a retry has to produce the same key to be any use. This matters more than it sounds: retry logic is where duplicate receipts come from.

Scheduling

Pass scheduled_at as an ISO 8601 timestamp and we hold the message until then. Your app does not need a cron job for it.

Body
"scheduled_at": "2026-09-04T07:00:00Z"

Look up a message

curl
curl https://digitalpigeon.golux.tech/v1/emails/01a06673-4d4b-7173-a296-487a2e8917b1 \
  -H "Authorization: Bearer dp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Response
{
  "id": "01a06673-4d4b-7173-a296-487a2e8917b1",
  "from": "Prodavnica <hello@mail.prodavnica.rs>",
  "to": ["kupac@example.com"],
  "subject": "Narudžbina #4471 je potvrđena",
  "last_event": "delivered",
  "created_at": "2026-09-03T08:47:07Z"
}

Errors

Every error has the same shape, and the message says what to change.

Shape
{
  "statusCode": 403,
  "name": "not_found",
  "message": "paypal.com is not one of your domains. Add it before sending from it."
}
StatusMeansRetry?
401 Key missing, wrong, or revoked. No — fix the key.
402 Out of allowance on your plan. After upgrading, or when the month resets.
403 Sending from a domain you have not verified. No — verify it first.
422 Something in the request is malformed. No — the message says what.
5xx Our problem. Yes, with backoff.

402 rather than 403 when you run out so your code can tell "pay and this works" apart from "you are not allowed" — the difference between a retry and an alarm at 3am.

Limits

Coming from somewhere else?

Moving from Resend · Sending from Lovable or Supabase · SMTP settings