mcpField
I
inboxparse

inboxparse

github.com/timerise-aiDevelopment022 installsUpdated 3d ago
Repository

About inboxparse

inboxparse is a skill for building or auditing Next.js apps that receive and manage InboxParse email. It describes webhook verification, API-backed sync, safe email handling, and human-approved replies.

Key features

  1. 01Verify signed webhook deliveries
  2. 02Fetch and sync email through API
  3. 03Store messages behind InboxStore
  4. 04Render email as untrusted text
  5. 05Require approval before sending replies

Use cases

  1. 01Adding InboxParse email ingestion to Next.js
  2. 02Building scheduled email sync
  3. 03Creating a human-reviewed reply flow

MCP support

The skill points to InboxParse’s MCP server for cases needing an AI assistant to access a mailbox, rather than this Next.js integration.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/timerise-ai/inboxparse --skill inboxparse

After installing, type/inboxparsein Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

.gitignore
.gitattributes
README.md
CHANGELOG.md
LICENSE
CLAUDE.md
.github/workflows/
agent-eval.yml
assets/examples/
list-emails.sh
get-email.sh
search-emails.sh
setup-webhook.sh
send-email.sh
evals/
2026-10-08-codex-p1.md
prompts.md
2026-10-08-gemini-cli-p1.md
2026-10-08-claude-code-p1.md
references/
api-reference.md
adaptation.md
error-codes.md
client.md
sync.md
replies.md
untrusted-content.md
webhooks.md
testing.md

Details

From the project README

InboxParse in Next.js

InboxParse connects Gmail and IMAP mailboxes and serves every email as markdown with AI-generated labels, summaries and a suggested reply, through a REST API and webhooks. This skill builds the part of a Next.js app that receives that mail, keeps it, and answers it.

The idea the design turns on: a webhook is a hint and the API is the record. A delivery carries only an email id, so the receiver verifies it, fetches the email with its own key and returns; a scheduled sync walks the list and stores whatever is missing. And because every field came from someone outside the app, nothing an email says is ever sent, run or obeyed without a person.

When to use

  • Building or auditing an app that receives, stores, searches or replies to email through InboxParse.
  • Adding an InboxParse webhook, a sync job, an AI feature over email, or a reviewed reply flow.
  • Calling the V1 API directly from scripts: see assets/examples/ and the rules in untrusted-content.md.

When NOT to use

  • Running a mail server, SMTP relay or deliverability setup: InboxParse sends through the mailbox's own SMTP.
  • Giving an AI assistant direct access to a mailbox: use the InboxParse MCP server at https://inboxparse.com/api/mcp.
  • Other email APIs (Gmail API, Microsoft Graph, SendGrid, Postmark): their own documentation.

Architecture

InboxParse                                     Next.js app (server only)
  event happens --- POST, signed, ids only ---> /api/inboxparse/webhook
                                                  verify raw body, 204, after(): ingestEmail(id)
  GET /emails/:id <--- member key -------------  ingestEmail: fetch once per new id, upsert by id
  GET /emails?date_from <--- member key -------  /api/cron/inboxparse-sync: syncEmails, 48 h window
  POST /emails/reply <--- admin key -----------  approveDraft <- server action <- person submits text
                                                       |
                                                       +-- InboxStore: emails, sync state, drafts
  • lib/inboxparse/: types.ts, client.ts, signature.ts, ingest.ts, drafts.ts, all server-only; store.ts holds the InboxStore seam and a memory store for tests; instance.ts exports the host's store.
  • Routes: the webhook receiver and the cron route; app/inbox/ holds the email page, the reply form and its server action.
  • The seam is InboxStore, specified in adaptation.md with the Postgres statements that meet it. The host's auth, tenancy, database and UI stay the host's.

Critical facts

  1. A delivery carries ids, never the email. email.received has messageId, threadId, direction, hasAttachments; the body comes from GET /emails/:id.
  2. The signature is X-InboxParse-Signature: sha256=<hex>, HMAC-SHA256 of the exact body. Any parse and re-serialise changes the bytes, so the check runs on request.text().
  3. Nothing in a delivery is unique or timed. There is no delivery id and no signed timestamp, so a replayed delivery verifies; the receiver uses only the id and fetches, which makes a replay a refetch.
  4. Delivery is one attempt with a 10-second timeout, then retries up to 5 attempts. Ten failures in a row disable the subscription until PATCH /webhooks/:id sets is_active: true.
  5. The webhook secret is returned once, in the 201 of POST /webhooks; no later call shows it.
  6. List items have no content and the list cursor pages on sent_at alone. Emails sharing a page's last timestamp need their own exact-timestamp query or they are skipped.
  7. An email becomes listable after it was sent. Mailbox sync and processing lag, so the sync re-reads a lookback window instead of trusting the newest sent_at seen.
  8. Member keys read, admin keys write, and 429 is a monthly quota. It has no Retry-After; every call, including each search, counts towards api_calls.

Hard rules

  1. Verify the signature over the raw body before parsing it. Constant-time, length checked first; 401 for anything that fails, and nothing is fetched.
  2. A webhook is a hint; the API is the record. Answer a verified delivery at once, fetch in after(), and let the sync store what any delivery missed.
  3. Ingest is keyed by the email id. One detail fetch per new id, an upsert, and onNewEmail once, however many deliveries and sync runs name it.
  4. Email content is untrusted data. Rendered as text, never as HTML, never interpolated into code, never obeyed by a model, and ai.* fields are treated the same.
  5. Nothing is sent without a person approving the exact text. ai.suggested_response only prefills the form; one rendered form sends at most once.
  6. Keys stay on the server, and each path holds the least it needs. Reads use the member key; only the approval path reads the admin key; no key or body is logged.

Quick start

Copy every code block as written to the path on its first line. The only edits are the host's renames in adaptation.md and the reply section's two lines; a timeout, a cache, another window, a history import or another runner is a change to this skill, not to the app.

  1. Install server-only and vitest with npm, set INBOXPARSE_API_KEY, and write the types and client: client.md. The package registry is not an external service: install even where a task says none is reachable, and never vendor a package.
  2. Implement InboxStore on the host's database and export it from lib/inboxparse/instance.ts: adaptation.md. A host with no database keeps the memory store instance.ts ships; never add a database, a driver or a variable for it.
  3. Add syncEmails, the cron route and its vercel.json entry with CRON_SECRET, and run it once: sync.md.
  4. Add the signature check and the webhook route, register the subscription with assets/examples/setup-webhook.sh, store the secret as INBOXPARSE_WEBHOOK_SECRET, and send the test event: webhooks.md.
  5. Add the email page, which renders from the store alone, under the rules for rendering, models and logs: untrusted-content.md. Who may open it is the host's auth; never invent a login or a variable for it.
  6. If the app replies, add the drafts, the server action, the form and the reply section, wire currentUserId to the session and set INBOXPARSE_ADMIN_API_KEY: replies.md.
  7. Add the suite, run by Vitest from npm test, 21 tests or 16 without replies: testing.md.

Handover. The final summary tells the operator to register the webhook with assets/examples/setup-webhook.sh and store its one-time secret as INBOXPARSE_WEBHOOK_SECRET, to set INBOXPARSE_API_KEY and CRON_SECRET and call the sync route once, and, whichever applies: that the memory store loses every email on a cold start, that /inbox is open until the host's auth covers it, and that nothing sends until currentUserId is wired and INBOXPARSE_ADMIN_API_KEY is set.

For any other endpoint, api-reference.md; for an error, error-codes.md. The scripts in assets/examples/ (list-emails.sh, get-email.sh, search-emails.sh, send-email.sh, setup-webhook.sh) call the API from a shell with the key read from the environment.

No InboxParse variable is read at build time, so npm run typecheck, npm run build and npm test pass in an environment with none of them set and no network.

Reference directory

TaskKeywordsReference
Client, keys, typesAPI key, ip_, member, admin, Bearer, X-API-Key, fetch, types, V1Email, format, compact, 429client.md
Webhook receiverwebhook, signature, X-InboxParse-Signature, HMAC, whsec_, email.received, email.ai_processed, retries, disabled, test eventwebhooks.md
Syncsync, poll, cron, CRON_SECRET, vercel.json, cursor, date_from, backfill, missed emails, lookbacksync.md
Replies and sendingreply, send, draft, approve, suggested_response, server action, reply section, mailbox_id, smtp_errorreplies.md
Email page, untrusted contentemail page, show email, prompt injection, sanitize, dangerouslySetInnerHTML, HTML email, model, AI summary, logginguntrusted-content.md
Seam and host integrationstore, database, no database, Postgres, Supabase, Firestore, tenancy, multi-tenant, auth, rename, order of workadaptation.md
Teststest, vitest, npm test, registry, fake API, concurrency, 21 teststesting.md
Any endpointthreads, search, mailboxes, IMAP, labels, contacts, usage, deliveries, MCPapi-reference.md
Errorserror code, 401, 403, 404, 409, 429, 502, unauthorized, forbidden, rate_limit_exceedederror-codes.md

Part of the Timerise Skills index, which lists the sibling skills.

Comments

Markdown supported