uCheckeruChecker
10 min read

Gmail Annotations: turning the Promotions tab into a storefront

Most marketing emails land in the Promotions tab. That is not a death sentence. Gmail Annotations let you attach rich cards, deal badges, and image carousels directly to your message there. Your email looks like a product listing instead of another grey text line. This guide covers the schema.org markup you need, the rendering rules Gmail enforces, and the mistakes that cause annotations to silently fail.


What Gmail Annotations actually are

Annotations are structured data fragments embedded in the HTML head of an email. They use a subset of schema.org vocabulary. When Gmail’s parser finds valid annotation markup, it renders a visual card in the Promotions tab: an image, a logo, a deal badge with a discount amount, or a carousel of product images. The email body is untouched. The annotation lives in the <head> section and stays invisible if the mail client does not support it.

Google introduced annotations in 2016 alongside the Promotions tab redesign. As of early 2026, they render on Gmail for Android, iOS, and the web client. Other mail clients ignore the markup entirely.

Two main annotation types exist: the single image promo card and the product carousel. Both are defined through JSON-LD blocks. Gmail also supports a deal badge overlay that shows a discount value or promo code.

Single image promo card

The simplest annotation. One image at the top of the email preview, plus an optional deal badge. Complete JSON-LD:

<script type="application/ld+json">
{
  "@context": "http://schema.org/",
  "@type": "PromotionCard",
  "image": "https://example.com/promo-header.png",
  "url": "https://example.com/sale",
  "headline": "Summer sale — 30% off everything",
  "sender": { "@type": "Organization", "name": "Example Store" },
  "discount": {
    "@type": "DiscountOffer",
    "description": "30% OFF",
    "discountCode": "SUMMER30",
    "availabilityStarts": "2026-06-01T00:00:00-07:00",
    "availabilityEnds": "2026-06-30T23:59:59-07:00"
  }
}
</script>

Place this block inside <head>, before the closing </head> tag. The image field is the hero banner. Gmail renders it at roughly 538×138 px on desktop, so horizontal images with a 3.9:1 aspect ratio work best. The image must be served over HTTPS.

The discount object is optional. When present, Gmail shows a green badge with the description text overlaid on the card. The availabilityStarts and availabilityEnds fields control when the badge is visible. Outside that window, Gmail hides the deal badge but still shows the image.

Product carousel

The carousel shows up to ten product images the user can swipe through without opening the email. Each item links to its own URL. In e-commerce this is the annotation type with the highest engagement, turning the Promotions tab into a browsable catalog.

<script type="application/ld+json">
[
  {
    "@context": "http://schema.org/",
    "@type": "PromotionCard",
    "image": "https://example.com/promo-header.png",
    "url": "https://example.com/sale",
    "headline": "New arrivals this week",
    "sender": { "@type": "Organization", "name": "Example Store" }
  },
  {
    "@context": "http://schema.org/",
    "@type": "PromotionCard",
    "image": "https://example.com/product-1.png",
    "url": "https://example.com/products/1",
    "headline": "Linen shirt — $49",
    "position": 1
  },
  {
    "@context": "http://schema.org/",
    "@type": "PromotionCard",
    "image": "https://example.com/product-2.png",
    "url": "https://example.com/products/2",
    "headline": "Cotton shorts — $35",
    "position": 2
  }
]
</script>

The first object is the “hero” card. Subsequent objects (with position values starting from 1) form the carousel items. Each needs its own image and url. Product images should be square (1:1) and at least 116×116 px; 200×200 or higher is better.

Deal badge without a promo image

Not every email has a banner. You can still add a deal badge:

<script type="application/ld+json">
{
  "@context": "http://schema.org/",
  "@type": "DiscountOffer",
  "description": "Free shipping",
  "availabilityStarts": "2026-05-01T00:00:00+03:00",
  "availabilityEnds": "2026-05-07T23:59:59+03:00"
}
</script>

This renders a small badge next to the subject line. No image, no carousel, just a green label that says “Free shipping.” Simple, but it catches the eye in a wall of undecorated subjects.

Technical requirements and gotchas

Gmail is strict about annotation parsing. If the markup is slightly off, the annotation disappears without any error message. What you need to get right:

  • Schema context must be HTTP. Use http://schema.org/, not https://schema.org. Gmail’s parser rejects the HTTPS variant. This trips up almost everyone the first time.
  • JSON-LD only. Microdata and RDFa are not supported. The block must be a <script type="application/ld+json"> tag inside <head>.
  • Images must be HTTPS. HTTP image URLs are silently ignored.
  • Image sizes. Hero: minimum 538×138 px, recommended 1076×276 px for retina. Carousel: minimum 116×116 px, square. Keep files under 1 MB.
  • ISO 8601 dates with timezone. 2026-06-01T00:00:00+03:00 works. 2026-06-01 does not. Omitting the timezone offset stops the deal badge from rendering.
  • Sender reputation. Gmail only renders annotations for senders with established reputation. New domains or domains with high complaint rates may see annotations ignored even when the markup is valid. Google does not publish the exact threshold.
  • One annotation block per email. Multiple <script> blocks: Gmail uses the first valid one and ignores the rest. Combine a carousel with a deal badge in one JSON array.

Testing annotations before sending

You cannot test annotations by sending from localhost. Gmail ties annotation rendering to sender authentication (SPF, DKIM, DMARC) and domain reputation.

1. Gmail Promotions Annotations Preview Tool

Google’s official preview tool validates the schema and shows a visual render. Paste your full HTML email source and it shows how the card will look. The fastest feedback loop during development.

https://developers.google.com/gmail/promotab/overview

2. Send a test from your production ESP

Use your actual sending infrastructure: same domain, same ESP, same authentication. Send to a personal Gmail account and check the Promotions tab. If the annotation does not render, open “Show original” and verify the JSON-LD block survived your ESP’s HTML processing. Some ESPs strip <script> tags from the head for security reasons.

If your ESP strips JSON-LD from the email head, annotations will never work through that provider. Check before investing time in markup.

ESPs known to preserve JSON-LD annotation blocks (as of early 2026):

  • Mailchimp (via custom code templates)
  • Klaviyo
  • Brevo (Sendinblue)
  • SendGrid (API sends)
  • Amazon SES
  • Postmark

Full working example

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Summer Sale</title>
  <script type="application/ld+json">
  {
    "@context": "http://schema.org/",
    "@type": "PromotionCard",
    "image": "https://cdn.example.com/email/summer-sale-hero.png",
    "url": "https://example.com/summer-sale?utm_source=email",
    "headline": "Summer sale starts now",
    "sender": { "@type": "Organization", "name": "Example Store" },
    "discount": {
      "@type": "DiscountOffer",
      "description": "25% OFF",
      "discountCode": "SUN25",
      "availabilityStarts": "2026-06-01T00:00:00+03:00",
      "availabilityEnds": "2026-06-15T23:59:59+03:00"
    }
  }
  </script>
</head>
<body><!-- your email body here --></body>
</html>

Three things to double-check: @context uses HTTP (not HTTPS), dates include timezone offsets, and the image URL is HTTPS and publicly accessible.


Annotations and deliverability

Annotations do nothing if your email does not reach the inbox. They sit on top of your existing deliverability infrastructure. The baseline conditions are the same as for any campaign: working SPF, DKIM, and DMARC, clean domain reputation, and a bounce rate below 2%. If you have already configured BIMI, your foundation is solid.

List hygiene matters here directly. Sending to dead addresses, spam traps, or disposable mailboxes raises bounce rate and damages domain reputation. Once reputation drops, Gmail stops rendering annotations even when the markup is correct. A dirty list kills reputation, no reputation means no annotations, no annotations means lower open rates. The cycle feeds on itself.

If you want to measure the effect, run an A/B test: the same email to two groups, one with annotations and one without. Compare open rates for Gmail addresses only. Senders with established reputation typically see lifts of 8-22%. The effect is stronger for e-commerce with product carousels, since users interact with the content before they open the email. For SaaS and B2B sends the lift is smaller but still positive. According to Google’s published data, emails with annotations see 10-20% higher open rates on average.

Annotations only work on a clean list

Gmail renders promo cards for domains with good reputation. Good reputation comes from a low bounce rate, few spam complaints, and consistent authentication. It starts with who you send to.

Before adding schema.org markup to your campaigns, make sure your list has no dead addresses or spam traps. Upload your list to uChecker and get 30 free checks to see where things stand. A clean list plus correct markup is what makes the Promotions tab work for you.

Gmail AnnotationsGmail promo cardsschema.org emailemail structured dataGmail Promotions tabemail deliverability