Developer docs
Deliver targeted ads over HMAC HTTPS
Integrate Meridian Ads with an SDK or signed HTTP. These pages are the English contract for publishers and integrators.
Meridian Ads is a real-time ad serving product from Yelle Software. Publishers send a user_key plus a small context map. Advertisers target those traits with prepaid USD credits and a CPM. Every creative is moderated before it can fill a live placement.
Intended production hosts: meridianads.tech for this console and these docs, api.meridianads.tech for the control plane, and serve.meridianads.tech for decision, events, and click redirects.
POST /v1/decision
Client
Redis index
Adserver
200 JSON or 204
Creative
Core serving endpoint
HMAC requiredHMAC required. 200 is a paid or house fill. 204 is no fill. 429 if the app_key exceeds 100 decisions per second.
https://serve.meridianads.tech/v1/decisionLocal http://localhost:8090/v1/decision
Request payload
The public app key prefix travels in X-App-Key, not in the JSON body.
| Field | Type | Required | Description |
|---|---|---|---|
| placement | string | Required | Placement name on the authenticated app. |
| user_key | string | Required | Opaque publisher identifier. Hashed at rest if it looks like email. Never put this on a landing URL. |
| context | object | Optional | Trait map merged last-write-wins. Max 8KB JSON. Missing traits fail closed. |
Code
SDK and cURLHMAC, timestamps, and the canonical string are handled by the client. Prefer an SDK. cURL is the raw contract.
import { MeridianAds } from '@meridianads/sdk'
const client = new MeridianAds({
appKey: process.env.MERIDIAN_ADS_APP_KEY,
secret: process.env.MERIDIAN_ADS_SECRET,
// baseUrl: 'http://localhost:8090'
})
const decision = await client.serve('home_banner', 'user-42', {
country: 'US',
plan: 'pro'
})
if (!decision) {
// HTTP 204: no paid or house fill
} else {
await client.recordImpression(decision)
}Response schemas
native_text and image Meridian Ads returns JSON. You render it. Formats are native_text and image. IDs below are synthetic examples for the docs.
native_text
JSON{
"campaign_id": "3b2e0c6a-4d11-4b0a-9c2e-0f1a2b3c4d5e",
"house": false,
"creative": {
"id": "7aa1b2c3-d4e5-6789-abcd-ef0123456789",
"format": "native_text",
"click_url": "https://serve.meridianads.tech/v1/click/{token}",
"impression_token": "imp_…",
"native": {
"headline": "Example native headline",
"body": "Synthetic copy for the docs.",
"cta": "Learn more"
}
}
}Example render
Example native headline
SyntheticSynthetic copy for the docs.
Learn more →
image
300×250{
"campaign_id": "3b2e0c6a-4d11-4b0a-9c2e-0f1a2b3c4d5e",
"house": false,
"creative": {
"id": "8bb2c3d4-e5f6-7890-bcde-f01234567890",
"format": "image",
"click_url": "https://serve.meridianads.tech/v1/click/{token}",
"impression_token": "imp_…",
"image": {
"cdn_url": "https://cdn.meridianads.tech/…",
"alt": "Example banner",
"width": 300,
"height": 250,
"mime": "image/webp"
}
}
}Example render
Example banner
cdn.meridianads.tech
Review gate
Live serve only uses approved creatives. If the moderator is down, the creative stays pending_review. Staff override is audited.
approved
Can fill live
pending_review
Waiting. Fail closed.
rejected
Cannot fill
Guides
Identifier contract
Opaque user_key, last-write-wins context, and what never leaves serve.
Segmentation
AND/OR trait trees. Missing traits fail closed. Empty rules match everyone.
OpenAPI
Control plane vs adserver, hosts, and the locked serve endpoints.
Serve HMAC
X-App-Key, X-Timestamp, X-Signature. Canonical string and clock skew.
Clicks
GET /v1/click/{token} records then 302s. Impression first. No user_key on landing.
Sandbox
mk_test_ keys run full moderation. They never enter the production index.
Locked serve loop
- 1
Authenticate with an app key prefix plus HMAC-SHA256.
- 2
Call POST /v1/decision with placement, user_key, and context.
- 3
Render the creative. Fire POST /v1/events with the impression token.
- 4
Send clicks through the signed click_url. Adserver 302s to the landing.