Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
To generate a dynamic Open Graph image whenever a webhook fires, accept and verify the event, save the fields your card needs, and expose a public image endpoint that renders those fields into a PNG. Set that endpoint’s absolute URL as the page’s og:image. For a Next.js app, Vercel’s ImageResponse is a direct rendering option; a hosted image API can avoid operating your own renderer.
How the pieces fit together
A webhook and an image endpoint do different jobs. The webhook is the trigger that tells your application something changed. Your application validates it and updates the data for a card. The image endpoint is the rendering boundary: when a crawler or browser requests a URL, it returns an image generated from the relevant data.
A typical flow is:
- A service sends an event such as a product update or release announcement to your webhook route.
- Your route authenticates the sender, checks the event shape, and stores or derives the small set of fields used in the image.
- Your site exposes a stable, publicly reachable image URL, such as
https://example.com/api/og/release-42. - The page’s HTML includes that absolute URL in
<meta property="og:image">. - A social platform fetches the image URL when it reads the page metadata.
Keep webhook processing separate from image rendering. A webhook should update state, not try to send a PNG to the social network. The crawler ordinarily discovers the image by fetching the page’s metadata and then requesting the URL in og:image.
Free tools Windows power users keep installed
One-click scans. No signup required.
Choose a rendering approach
| Approach | Best fit | Trade-off |
|---|---|---|
Next.js ImageResponse / @vercel/og |
A team already deploying a Next.js app or Vercel Functions that wants control of its templates. | You operate the route, validation, data access, and cache behavior. The documented renderer supports a subset of CSS rather than a full browser layout engine. |
| Satori-based implementation | A framework-agnostic service that needs direct control of the renderer. | You must integrate SVG-to-PNG conversion and account for the renderer’s supported CSS. |
| Hosted OG image API, such as OGKit | A team that prefers URL parameters, templates, edge execution, and caching without running a renderer. | You trade infrastructure work for vendor dependence; check current limits, pricing, and program terms before choosing. |
Vercel’s documentation recommends a 1200 × 630 pixel OG image. It describes @vercel/og as using Satori and Resvg to convert HTML and CSS to PNG. This is a recommended size, not a guarantee that every platform will display the image identically.
#1 Best Overall
Build a Next.js image endpoint
The following App Router route accepts a title and author as query parameters and returns a PNG. Install @vercel/og in the Next.js project, then create app/api/og/route.tsx:
import { ImageResponse } from '@vercel/og';
export const runtime = 'edge';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const title = (searchParams.get('title') ?? 'Untitled update').slice(0, 120);
const author = (searchParams.get('author') ?? 'Your team').slice(0, 60);
return new ImageResponse(
(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
padding: '64px',
background: '#101828',
color: '#ffffff',
fontFamily: 'sans-serif',
}}
>
<div style={{ fontSize: 26, color: '#98a2b3' }}>PRODUCT UPDATE</div>
<div style={{ fontSize: 62, fontWeight: 700, lineHeight: 1.1 }}>
{title}
</div>
<div style={{ fontSize: 24, color: '#d0d5dd' }}>By {author}</div>
</div>
),
{ width: 1200, height: 630 }
);
}
For example, request https://example.com/api/og?title=Spring%20release&author=Jamie. URL-encode values when constructing the request. Keep the inputs short and constrained: apart from making text fit, this prevents unbounded data from being passed into a public rendering route. The code’s character limits are a simple display safeguard, not a substitute for validating your application’s event schema.
The renderer supports flexbox and a subset of CSS; do not assume browser CSS features such as CSS Grid will work. Its documented font formats are TTF, OTF, and WOFF, with TTF or OTF preferred for parsing speed. The documented maximum bundle size is 500 KB, including JSX, CSS, fonts, images, and other assets. Keep decorative assets and font files within those limits.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Connect a webhook safely
The route that receives events should verify authenticity before changing the data used by a card. The exact signature scheme depends on the webhook provider, so use that provider’s documented verification procedure rather than accepting an arbitrary shared secret or trusting a field in the request body. The following illustrates the separation; verifyProviderSignature and saveRelease represent provider- and application-specific implementations, not built-in Next.js functions.
export async function POST(request: Request) {
const rawBody = await request.text();
const signature = request.headers.get('x-provider-signature');
if (!signature || !(await verifyProviderSignature(rawBody, signature))) {
return new Response('Invalid signature', { status: 401 });
}
let event: unknown;
try {
event = JSON.parse(rawBody);
} catch {
return new Response('Invalid JSON', { status: 400 });
}
// Validate the event against the provider's schema here.
// Select only fields needed by the card, then persist them.
const release = selectAndValidateReleaseFields(event);
if (!release) return new Response('Invalid event', { status: 400 });
await saveRelease(release);
return Response.json({ received: true });
}
Do not treat a webhook payload as safe just because it came from a service you use. Authenticate it, validate its structure and size, and select only fields needed for the image. Escape or safely render text rather than inserting payload text as raw markup. If templates load remote images, constrain allowed URLs or use trusted assets so an attacker cannot make your renderer fetch arbitrary locations.
For production, a stable route keyed by a record ID is often easier to secure and cache than a URL containing arbitrary user-provided text. In that design, the endpoint looks up the validated record and renders its stored fields. Do not put secret or private webhook data in a publicly fetchable image URL.
Publish metadata crawlers can fetch
On the page being shared, publish an absolute URL in the Open Graph metadata:
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches<meta property="og:image" content="https://example.com/api/og/release-42" />
The image URL must be reachable by the social crawler without a browser login or private network access. The route should return an image response rather than an HTML page, and should not require a user session. Vercel recommends allowing OG routes in robots.txt, for example:
Rank #3
User-agent: *
Allow: /api/og/*
Open Graph image metadata is the convention used by social preview consumers. It does not mean every consumer refreshes immediately or displays the image in exactly the same way. Social-network cache invalidation guarantees and independent performance benchmarks are not established here.
Keep images fresh without wasting rendering work
Make image URLs deterministic: the same validated record and template version should produce the same URL. That gives caches a useful key. When webhook data changes, either invalidate the relevant cached response through your hosting setup or change the URL, for example by including a record revision or template version. A versioned URL is usually the clearest way to ensure that a crawler requesting a new URL sees updated content, though each social platform controls its own fetch and cache schedule.
OGKit documents a 24-hour CDN cache and edge execution for repeated parameter combinations. That is a vendor-documented behavior, not a universal cache lifetime for every hosted API or social platform. Confirm current service terms before relying on a provider’s caching behavior.
Recommended Free Tools
For a self-hosted endpoint, verify how your deployment handles caching and make the cache policy match your update pattern. A long-lived cache can reduce repeated rendering but leave old cards visible after an update. A short cache can improve freshness but cause more image requests and rendering work. The right setting depends on how often the underlying data changes and whether a URL changes with it.
Rank #4
Troubleshoot missing or stale previews
- The preview has no image: Inspect the page’s delivered HTML and confirm that
og:imagecontains an absolute, public URL. Request that URL without a login and confirm that it returns the image, not an error page or redirect to private content. - The image route returns an error: Check the deployment logs and the renderer’s CSS and asset requirements. Remove unsupported layout features such as CSS Grid, reduce bundled assets if the documented 500 KB ceiling is exceeded, and confirm that fonts use a supported format.
- Text is clipped or unreadable: Shorten or constrain variable strings, adjust the flex layout and font sizes, and test worst-case titles rather than only typical ones. Keep the image’s intended 1200 × 630 dimensions consistent with the response.
- The card shows old webhook data: Confirm the webhook was authenticated, passed validation, and updated the record actually used by the image route. Then account for your own cache and the social platform’s cached preview; use a changed, versioned image URL when appropriate.
- The crawler cannot retrieve the endpoint: Check that the route is publicly reachable and not blocked by authentication, network restrictions, or crawler rules. Vercel recommends allowing the OG route in
robots.txt. - The webhook is rejected: Verify the provider’s signature against the exact raw request body as its documentation requires. Check the incoming schema and reject malformed events rather than silently rendering partial or untrusted values.
Or skip the browser setup
ScreenshotNeo is a website screenshot API, not an Open Graph template renderer: use the workflow above when you need to generate a designed card from webhook fields. If your webhook workflow also needs a screenshot of a rendered public page, one GET request returns an image or PDF. See the ScreenshotNeo API docs.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Learn about ScreenshotNeo.
Sign up free for 1,000 screenshots a month, with no card required.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Frequently Asked Questions
Does a webhook itself send the OG image to LinkedIn, Slack, Facebook, or X?
No. It updates the data your image endpoint uses. Each platform discovers the image by fetching the page metadata and the public URL in `og:image`; fetching and refresh timing are controlled by that platform.
Can I use a ScreenshotNeo screenshot as the dynamic OG image?
ScreenshotNeo captures a webpage; it is not a renderer for a custom card built from webhook fields. Use a template-rendering endpoint such as the Next.js approach above for that job.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

