preping

API reference

Base URL https://preping.com. Authenticate with Authorization: Bearer pp_live_… (or pp_test_…, which never emails real people). JSON in, JSON out.

Errors always tell you how to fix them

{
  "error": {
    "code": "domain_not_verified",
    "message": "You can't send from acme.com yet because it isn't verified.",
    "fix": "Finish adding the DNS records for acme.com. We check automatically every few minutes.",
    "link": "https://preping.com/app/domains/dom_…"
  }
}

Status codes: 202 accepted · 401 bad key · 403 not allowed (with the reason) · 404 · 409 conflict · 422 invalid input · 429 limit reached (with when it resets) · 500 our fault (safe to retry with an Idempotency-Key).

POST/v1/emailsSend an emailsending-only keys allowed
{
  "from": "Acme <hello@acme.com>",
  "to": [
    "asha@example.com"
  ],
  "subject": "Your receipt",
  "html": "<p>Thanks!</p>",
  "reply_to": "support@acme.com",
  "tags": {
    "order": "1042"
  },
  "scheduled_at": "in 1 hour",
  "stream": "transactional"
}

Or use a template: { "template": "welcome", "variables": { "first_name": "Asha" } }. Attachments: [{ filename, content (base64), content_type }]. Optional: cc, bcc, headers, track_opens, track_clicks. Header Idempotency-Key makes retries safe.

POST/v1/emails/batchSend up to 100 emailssending-only keys allowed

Body: an array of email objects. Each succeeds or fails on its own; results come back in order.

GET/v1/emails/{id}Get an email, with its full event timeline
GET/v1/emails?limit=20&before={id}List emails, newest first
POST/v1/emails/{id}/cancelCancel a scheduled emailsending-only keys allowed
POST/v1/contactsCreate or update a contact (by email)
{
  "email": "asha@example.com",
  "first_name": "Asha",
  "attributes": {
    "plan": "pro"
  },
  "lists": [
    "lst_…"
  ]
}
GET/v1/contacts/{id or email}Get a contact and their lists
PATCH/v1/contacts/{id or email}Update a contact
{
  "attributes": {
    "plan": "paid"
  },
  "subscribed": true
}
DELETE/v1/contacts/{id or email}Delete a contact
POST/v1/eventsTrack an event (starts automations)sending-only keys allowed
{
  "email": "asha@example.com",
  "name": "signed_up",
  "properties": {
    "plan": "trial"
  }
}
POST/v1/validationsCheck an address before sending (free, sends nothing)sending-only keys allowed
{
  "email": "asha@gmial.com"
}

Returns result (valid / risky / invalid), a plain-English reason, the checks (syntax, mail server, disposable, role address) and a typo suggestion like asha@gmail.com. Send { "emails": [...] } for up to 50 at once.

GET/v1/listsList your lists
GET/v1/templatesList templates and the variables each one needs
GET/v1/domainsList domains with DNS record status
POST/v1/domainsAdd a domain
{
  "name": "acme.com"
}
POST/v1/domains/{id}/verifyRe-check a domain's DNS now
GET/v1/suppressionsList suppressed addresses
POST/v1/suppressionsSuppress an address
{
  "email": "no@example.com",
  "scope": "marketing"
}
DELETE/v1/suppressions/{email}Remove a suppression

Webhook events

Signed with Standard Webhooks. Payload: { type, created_at, data }.

email.sentWe handed the email to the receiving server
email.deliveredThe receiving server accepted it
email.delivery_delayedDelivery is being retried (temporary problem)
email.bouncedIt couldn't be delivered
email.complainedThe recipient marked it as spam
email.openedThe recipient opened it
email.clickedThe recipient clicked a link
email.failedWe couldn't send it (see error)
email.suppressedNot sent: recipient is on your suppression list
contact.createdA contact was added
contact.unsubscribedA contact unsubscribed
domain.verifiedA domain finished verifying
domain.driftedA domain's DNS records changed and need attention
campaign.sentA campaign finished sending
inbound.receivedAn email arrived at one of your inbound addresses

SMTP

Host smtp.preping.com, port 587 (STARTTLS) or 465, username preping, password: any API key.

Email API reference · Preping