Skip to content
ParsedMail

Email parser quickstart

To get your first parsed email, create an inbox, describe the fields you need in plain English, then forward an email to the inbox address or POST it to the API. ParsedMail returns a JSON object with your keys, typed values and a confidence score per field, by webhook or from the REST API.

Create an account and confirm your address

Sign up with an email address and a password, then enter the code we send to that address. Confirming the address unlocks a one-time sandbox of 10 emails with one inbox. The sandbox reads attachments of one page and no scans, and keeps raw emails for 7 days. It is not a plan and it does not renew. Webhook delivery starts with a paid plan, so in the sandbox you read results in the dashboard or with the API.

Everything below works the same on Starter, Growth and Scale. The plan limits are listed in errors and limits.

Create an inbox and a schema

In the dashboard open Inboxes and create one per workflow, for example "Orders". Each inbox gets its own address made of the name and a short random suffix:

[email protected]

Then open Schemas, create a schema and describe the fields in one sentence:

order number, customer name, customer email, products with quantity and price, total, delivery date

ParsedMail turns the sentence into typed fields that you can adjust before saving:

order_number     string
customer_name    string
customer_email   email
products         table (name string, quantity integer, price money)
total            money
delivery_date    date

Attach the schema to the inbox. The same steps work through the API with POST /v1/schemas and POST /v1/inboxes. Field types, tables and versioning are covered in defining schemas.

Send your first email

The quickest test is to forward any order confirmation from your mailbox to the inbox address. For a steady flow, set up a Gmail filter, an Outlook rule or a mail server alias as described in inboxes and forwarding.

If the email already sits in your own system, POST it to the inbox with an API key that has the parse scope:

curl https://parsedmail.com/api/v1/inboxes/inb_8k2m4q7d1x/emails \
  -H "Authorization: Bearer $PARSEDMAIL_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "[email protected]",
    "subject": "Order #NW-48213 confirmed",
    "text": "Order number: NW-48213\nCustomer: Dana Ortiz\n2 x Burr grinder K6 @ $189.00\nTotal: $378.00\nEstimated delivery: October 21, 2026"
  }'

The API answers right away with 202 Accepted and the email id. Parsing runs in the background and usually takes a few seconds:

{"id": "eml_f0qfttfnggyv5cn4a3ka", "object": "email", "status": "received"}

Raw MIME files, pasted text and base64 attachments are covered in sending emails.

Read the parsed result

The email appears in the dashboard log with the original next to the JSON. With the API, fetch it by id:

curl https://parsedmail.com/api/v1/emails/eml_f0qfttfnggyv5cn4a3ka \
  -H "Authorization: Bearer $PARSEDMAIL_KEY" \
  -H "Accept: application/json"
{
  "id": "eml_f0qfttfnggyv5cn4a3ka",
  "object": "email",
  "inbox": "inb_8k2m4q7d1x",
  "inbox_address": "[email protected]",
  "schema": "sch_vnf3updh2qin",
  "schema_version": 1,
  "status": "parsed",
  "received_at": "2026-10-15T09:12:04+00:00",
  "from": {"email": "[email protected]", "name": null},
  "to": "[email protected]",
  "subject": "Order #NW-48213 confirmed",
  "message_id": null,
  "duplicate_of": null,
  "attachments": [],
  "data": {
    "order_number": "NW-48213",
    "customer_name": "Dana Ortiz",
    "customer_email": null,
    "products": [
      {"name": "Burr grinder K6", "quantity": 2, "price": {"amount": 189, "currency": "USD"}}
    ],
    "total": {"amount": 378, "currency": "USD"},
    "delivery_date": "2026-10-21"
  },
  "confidence": {"order_number": 0.99, "customer_name": 0.95, "customer_email": 0.5, "products": 0.96, "total": 0.98, "delivery_date": 0.94},
  "confidence_avg": 0.964,
  "needs_review": false,
  "reviewed": false,
  "classification": null,
  "warnings": [],
  "sandbox": false
}

A value that is not in the email is null (an empty array for lists and tables), never a guess. When the status is still received or processing, wait a moment and fetch again, or let the webhook tell you.

Add a webhook

On a paid plan open Webhooks, add an https URL and copy the signing secret that starts with whsec_. Every parsed email is POSTed to that URL as JSON with a ParsedMail-Signature header. A minimal receiver in Node:

import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/webhooks/parsedmail", express.raw({ type: "application/json" }), (req, res) => {
  const parts = Object.fromEntries(
    (req.get("ParsedMail-Signature") || "").split(",").map((p) => p.trim().split("="))
  );
  const expected = crypto
    .createHmac("sha256", process.env.PARSEDMAIL_WEBHOOK_SECRET)
    .update(`${parts.t}.${req.body}`)
    .digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
  if (!fresh || expected !== parts.v1) return res.sendStatus(400);

  const event = JSON.parse(req.body);
  console.log(event.type, event.data.id, event.data.data);
  res.sendStatus(200);
});

app.listen(3000);

Press Send test event to check the endpoint. Use a constant time comparison in production; complete examples for Node, Python, PHP and Go and the retry schedule are in webhooks.

Create an API key

Open API keys, give the key a name and pick its scopes. The key belongs to the workspace you are in when you create it and is shown once, so store it in your secret manager right away.

ScopeAllows
readList and fetch inboxes, schemas and emails
parseSubmit emails, replay emails, call POST /v1/parse
writeCreate inboxes, create and update schemas, delete emails

New keys get read and parse by default. Check the key with the usage endpoint, which every key may call:

curl https://parsedmail.com/api/v1/usage \
  -H "Authorization: Bearer $PARSEDMAIL_KEY" \
  -H "Accept: application/json"
{"plan": "starter", "emails_used": 12, "emails_limit": 1000, "pages_used": 4, "pages_limit": 300, "period_start": "2026-10-03", "period_end": "2026-11-03"}

The Accept: application/json header in these examples is optional; the API always answers in JSON. Every endpoint is described in the API reference.

Frequently asked questions

Do I need to write code to use ParsedMail?

No. You can create inboxes and schemas, forward emails and read the JSON in the dashboard, and send results to Zapier, Make or n8n with their built-in webhook trigger. The API is there when you want to call it from your own code.

How long does parsing take?

Most emails are parsed within a few seconds of arrival. Emails with large PDFs take longer because every page is read.

Can I test without forwarding real email?

Yes. Paste a sample email in the schema editor and press Test, or POST a JSON body to /v1/inboxes/{id}/emails. Each test run counts as one email.

What happens when my inbox has no schema yet?

The email is stored with status failed and the message "This inbox has no schema yet". Attach a schema and replay it from the log or with POST /v1/emails/{id}/replay.

Related