Every domain you own and every hosting plan you pay for sends a renewal email the moment the registrar or host charges your card for the next cycle — and each one writes the message in its own template. GoDaddy puts the expiration date in the subject line and the renewal term in a footnote, Namecheap embeds the period length in a bullet list, Cloudflare writes the renewal in a single-paragraph receipt, Squarespace hides the auto-renew toggle inside a two-column layout, AWS Route 53 sends a quiet “your domain registration has been renewed” line inside a longer billing notification. The fields a domain-portfolio dashboard, an expiration-alerting workflow, or an annual spend rollup needs are the same every time: which registrar, which domain, how long was added to the registration, how much was charged, in which currency, when the new expiration date lands, whether auto-renew is on, and whether the charge succeeded.
MailFrame extracts those fields from the renewal email into typed, schema-validated JSON you can route straight into a domain tracker, a whois-refresh queue, or an “expiring in the next 30 days” alert.
currency is normalized to lower-case ISO 4217 — usd, eur,
gbp, cad, aud, jpy, etc. — so a downstream rollup never has
to guess about case when grouping multi-currency renewals.
It works on the body of “your domain has been renewed”, “your hosting plan
has been renewed”, and “your domain registration has been extended”
emails — typically sent from noreply@<registrar>, billing@<registrar>,
or support@<registrar> addresses. Match on the message body and the
renewal-specific wording (“renewed”, “extended”, “registration period”,
“new expiration date”, “auto-renew”) rather than the From address alone
— registrars rotate notification domains and add tenant-specific subdomains
over time.
Fields MailFrame extracts
| Field | Type | Example | Notes |
|---|---|---|---|
registrar_name | string | GoDaddy.com, LLC | The registrar or hosting company on the email |
registrar_domain | string | godaddy.com | Sending or billing domain |
domain_name | string | acmewidgets.com | The domain that was renewed (lowercased, no leading www.) |
registration_period_years | integer | 1 | Number of years added to the registration in this renewal cycle |
previous_expiration_date | string | 2026-07-15 | Expiration date before this renewal, ISO 8601 |
new_expiration_date | string | 2027-07-15 | Expiration date after this renewal, ISO 8601 |
amount_cents | integer | 1999 | Amount charged in minor units, currency-agnostic |
currency | string | usd | ISO 4217, normalized to lower-case (e.g. usd, eur, gbp) |
auto_renew | enum | enabled | One of enabled, disabled, unknown |
status | enum | renewed | One of renewed, payment_failed, expired, transferred, cancelled, changed |
privacy_protection | enum | enabled | One of enabled, disabled, unknown — WHOIS privacy / proxy status when printed |
payment_method_brand | string | visa | Card brand, normalized — visa, mastercard, amex, discover, etc. |
payment_method_last4 | string | 4242 | Last 4 digits of the card on file |
manage_url | string | https://dcc.godaddy.com/manage/acmewidgets.com | ”Manage domain” link when present |
invoice_url | string | https://dcc.godaddy.com/billing/invoice/INV-2026-7711 | Link to the per-cycle invoice or receipt when present |
account_email | string | jordan.rivera@example.com | The recipient on the email — the account holder |
date | string | 2026-07-12 | Date the renewal email was sent, ISO 8601 |
Sample input
A typical GoDaddy domain renewal email looks like this:
From: noreply@godaddy.com
Subject: acmewidgets.com has been renewed
Date: Sun, 12 Jul 2026 09:02:11 -0700
To: jordan.rivera@example.com
Hi Jordan,
Your domain acmewidgets.com has been renewed successfully.
Registration period: 1 year
New expiration date: July 15, 2027
Auto-renew: Enabled
Privacy protection: Enabled
Amount charged: $19.99 USD
Payment method: Visa ending in 4242
Invoice: INV-2026-7711
Manage your domain:
https://dcc.godaddy.com/manage/acmewidgets.com
View invoice:
https://dcc.godaddy.com/billing/invoice/INV-2026-7711
The same shape is produced for a Namecheap renewal, a Cloudflare domain
registration renewal, a Squarespace domain or website plan renewal, or an
AWS Route 53 domain registration renewal — registrar_name and
registrar_domain carry the billing party, registration_period_years
captures the term length in years, previous_expiration_date and
new_expiration_date are computed from the term and the prior expiry when
the email prints only one, auto_renew is normalized across vendors that
phrase it differently (“auto-renew on”, “auto-renewal enabled”, “renew
automatically”), and status distinguishes a successful renewed
confirmation from a payment_failed notice (alert on this!), an expired
notice, a transferred notice (the domain left the registrar), a
cancelled confirmation, or a changed notice when the period length or
privacy setting has shifted.
Structured JSON output
{
"registrar_name": "GoDaddy.com, LLC",
"registrar_domain": "godaddy.com",
"domain_name": "acmewidgets.com",
"registration_period_years": 1,
"previous_expiration_date": "2026-07-15",
"new_expiration_date": "2027-07-15",
"amount_cents": 1999,
"currency": "usd",
"auto_renew": "enabled",
"status": "renewed",
"privacy_protection": "enabled",
"payment_method_brand": "visa",
"payment_method_last4": "4242",
"manage_url": "https://dcc.godaddy.com/manage/acmewidgets.com",
"invoice_url": "https://dcc.godaddy.com/billing/invoice/INV-2026-7711",
"account_email": "jordan.rivera@example.com",
"date": "2026-07-12"
}
JSON Schema definition
Every field is validated against the schema before MailFrame returns it.
You can copy this as a starting point and tighten it for your own use
case — for example, require payment_method_last4 if every renewal in
your portfolio tracker must carry a card fingerprint, or restrict
registration_period_years to {"1", "2", "3", "5", "10"} if you only
care about the standard registration terms:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "domain_hosting_renewal",
"type": "object",
"required": [
"registrar_name",
"domain_name",
"new_expiration_date",
"amount_cents",
"currency",
"status"
],
"properties": {
"registrar_name": { "type": "string", "minLength": 1 },
"registrar_domain": { "type": "string", "format": "hostname" },
"domain_name": { "type": "string", "format": "hostname" },
"registration_period_years": { "type": "integer", "minimum": 1, "maximum": 10 },
"previous_expiration_date": { "type": "string", "format": "date" },
"new_expiration_date": { "type": "string", "format": "date" },
"amount_cents": { "type": "integer", "minimum": 0 },
"currency": { "type": "string", "minLength": 3, "maxLength": 3, "pattern": "^[a-z]{3}$" },
"auto_renew": {
"type": "string",
"enum": ["enabled", "disabled", "unknown"]
},
"status": {
"type": "string",
"enum": ["renewed", "payment_failed", "expired", "transferred", "cancelled", "changed"]
},
"privacy_protection": {
"type": "string",
"enum": ["enabled", "disabled", "unknown"]
},
"payment_method_brand": { "type": "string" },
"payment_method_last4": { "type": "string", "pattern": "^[0-9]{4}$" },
"manage_url": { "type": "string", "format": "uri" },
"invoice_url": { "type": "string", "format": "uri" },
"account_email": { "type": "string", "format": "email" },
"date": { "type": "string", "format": "date" }
}
}
Parse via the API
POST the raw email (MIME or plain text) to /v1/parse with the schema you
want to extract against:
curl https://api.mailframe.ai/v1/parse \
-H "Authorization: Bearer ${MAILFRAME_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"schema_id": "domain_hosting_renewal",
"raw_mime": "From: noreply@godaddy.com\r\nTo: jordan.rivera@example.com\r\nSubject: acmewidgets.com has been renewed\r\nDate: Sun, 12 Jul 2026 09:02:11 -0700\r\n\r\nHi Jordan,\r\n\r\nYour domain acmewidgets.com has been renewed successfully.\r\n\r\nRegistration period: 1 year\r\nNew expiration date: July 15, 2027\r\nAuto-renew: Enabled\r\nPrivacy protection: Enabled\r\n\r\nAmount charged: $19.99 USD\r\nPayment method: Visa ending in 4242\r\nInvoice: INV-2026-7711\r\n\r\nManage your domain:\r\nhttps://dcc.godaddy.com/manage/acmewidgets.com\r\n\r\nView invoice:\r\nhttps://dcc.godaddy.com/billing/invoice/INV-2026-7711"
}'
MailFrame returns the typed JSON in the same HTTP response. Prefer asynchronous
delivery? Signed webhook delivery — where MailFrame POSTs the extraction result to
your endpoint with an HMAC-SHA256 signature in the MailFrame-Signature header and
exponential-backoff retries — is available with allowlist onboarding; request
enablement from the dashboard under Settings → Webhooks, or contact
support@mailframe.ai for early-access access.
Operational notes
- Dedup on
domain_name+new_expiration_date. A single domain renewal produces one email per cycle (initial renewal confirmation, optional receipt when the invoice closes, optional reminder a few days ahead). Grouping on(domain_name, new_expiration_date)keeps a long-lived domain from creating multiple rows per cycle in your portfolio tracker. - Alert on
payment_failed. Apayment_failedstatus means the card on file was declined. Treat it as a high-priority routing signal — page ops or open a registrar support ticket immediately — instead of archiving it alongside successful renewals. A failed renewal can cascade into domain expiration and service downtime within hours for production domains. - Alert on
auto_renew: disabledfor production domains. Adisabledauto-renew on a critical domain is a regression risk. Surface these to ops even whenstatus: renewedis set, so the team can re-enable auto-renew before the next cycle ends. - Page on
expiredortransferred. Anexpirednotice means the registration lapsed and recovery may now require a redemption fee. Atransferrednotice means the domain left the registrar entirely — treat as a high-priority signal for any domain you expected to control. - Surface
cancelled. Acancellednotice means the auto-renew was turned off without the renewal going through. The domain will lapse on the existingnew_expiration_dateunless re-enabled before then. Alert on the ops channel the same way as a disabled auto-renew. - Log
changedfor audit. Achangednotice means the registration term length or privacy setting shifted on renewal — typicallyregistration_period_yearsdiffers from the prior cycle orprivacy_protectionflipped. Surface to the audit log even when the new expiration date is unchanged. previous_expiration_dateandnew_expiration_dateare computed independently. Some registrars print only the new date; MailFrame computesprevious_expiration_datefromnew_expiration_dateminusregistration_period_yearsso downstream “expiring in the next 30 days” alerts can use either field as the source of truth.- Body-only today. MailFrame reads the fields printed in the email body. PDF,
image, and calendar-attachment input are on the roadmap; inbox forwarding —
pointing your registrar’s notification forwarding at a unique inbox address
MailFrame assigns you — is planned as well. Until those ship, POST the raw
email to
/v1/parseas shown above.
Parsing other transactional mail? See the subscription renewals schema for SaaS billing renewals, the invoices schema for one-off vendor billing, the shipping notifications schema for carrier tracking, browse the full schema library, or read the API docs and pricing for the free-tier limits.