Serve Endpoint

The unified ad server endpoint. Every page load where an Inlay embed script is present calls this endpoint to resolve ad content for the current page.

POST/api/serve/{siteScriptId}

Called by the embed script

You do not call this endpoint directly. The embed script calls it automatically on every page load. This reference documents the request/response contract for debugging purposes.

Overview

The serve endpoint is the core of Inlay's ad delivery pipeline. In a single request, it resolves the site, matches a placement, runs an auction, renders the winning creative into the publisher's template, records the impression, and returns the complete rendered HTML — all server-side. No raw creative data is ever exposed to the client.

Request

Headers

http
Content-Type: application/json

Body

json
{
  "url": "https://example.com/blog/my-post",
  "domStructure": {
    "selector": "article .content p",
    "position": "after",
    "count": 8,
    "samples": [
      "<p class="body-text">The first paragraph...</p>"
    ]
  },
  "previewToken": "cld_abc123..."  // optional — present only in preview mode
}
FieldTypeDescription
urlstringFull URL of the current page, including query string. Used for URL pattern matching.
domStructureobject | nullDOM snapshot captured by the embed script. Used for AI template generation on the first visit.
domStructure.selectorstringCSS selector that identifies the target element.
domStructure.positionstring"before" or "after" — where relative to the selector the ad is injected.
domStructure.countnumberNumber of elements matching the selector on the page.
domStructure.samplesstring[]Array of outer HTML strings for up to 5 matching elements.
previewTokenstring?If present, routes through preview branch. Value is the placement's previewToken.

Response

Not available

Returned when no placement matches, the template is not yet approved, or no ad won the auction:

json
{ "available": false }

Ad available

json
{
  "available": true,
  "html": "<div class="inlay-ad">...</div>",
  "selector": "article .content p",
  "position": "after",
  "placementId": "clp_abc123",
  "clickUrl": "https://useinlay.com/api/track/click?pid=...&dest=...",
  "impressionTrackers": [],
  "beaconUrl": "https://useinlay.com/api/track"
}
FieldDescription
htmlServer-rendered HTML of the complete ad component (template + creative merged). Safe to set as innerHTML directly.
selectorCSS selector for the injection target element.
position"before" or "after" — relative to the selector.
placementIdPlacement identifier. Used for click tracking.
clickUrlPre-built click tracking URL. Embedded in the rendered HTML — not for direct use.
impressionTrackersArray of third-party impression pixel URLs. The embed script fires these as 1×1 image requests.
beaconUrlBase URL for impression/click beacons fired by the embed script.

Preview response

In preview mode, the response includes two additional fields:

json
{
  "available": true,
  "html": "...",
  "isPreview": true,
  "previewToken": "cld_abc123...",
  "beaconUrl": null,
  "impressionTrackers": []
}

isPreview: true signals to the embed script to render the floating approval panel. beaconUrl: null suppresses impression tracking in preview mode — previews are not counted as real impressions.

Request flow

  1. Rate limit check — 120 requests per minute per IP. Returns 429 if exceeded.
  2. Resolve site — looks up the site by siteScriptId. Returns { available: false } if not found or inactive.
  3. Preview branch — if previewToken is present, finds placement by token and returns template with sample creative. Skips all remaining steps.
  4. URL pattern matching — finds the first active placement whose URL patterns match body.url.
  5. Template generation — if no template exists and domStructure is provided, the AI generates one and saves it. Returns { available: false } (pending approval).
  6. Approval gate — if template exists but is not approved, returns { available: false }.
  7. Auction — runs SSP adapters in parallel. If no SSP bid clears the floor, falls back to a house ad.
  8. Render — merges winning creative into the template HTML server-side.
  9. Record impression — writes to AggregatedMetrics; records publisher revenue at 70% of the winning CPM (SSP wins only — house ads generate no revenue).
  10. Respond — returns rendered HTML + placement metadata.

Rate limiting

The serve endpoint is limited to 120 requests per minute per IP address. This is a per-instance sliding window counter — on serverless deployments, limits are applied per warm function instance, not globally.

When the limit is exceeded, the endpoint returns:

http
HTTP/1.1 429 Too Many Requests
Retry-After: 47

Error responses

StatusCause
200 + available: falseSite not found, inactive, no matching placement, template pending or unapproved, auction with no winner
429 Too Many RequestsRate limit exceeded. Retry after Retry-After seconds.