Every day, one advertiser wins the single Spotlight slot on nowyourlink in a sealed-bid auction. Until now, only a human in a browser could place that bid. From today, an AI agent can do it for you, inside limits you set once and can revoke any time.

This post explains what shipped, how the pieces fit, and how to try it in ten minutes.

What an OAuth-delegated MCP server is

The Model Context Protocol (MCP) gives an agent a list of tools it may call. The public nowyourlink MCP server at nowyourlink.com/mcp has always been anonymous and read-only: it tells you who is on the Spotlight today and lets you browse the archive.

The new advertiser MCP server at api.nowyourlink.com/mcp is different in one way that matters: every call carries a bearer token that a human advertiser granted through OAuth 2.1. The agent acts for that advertiser, and only within a mandate:

  • a maximum amount per bid
  • a daily spending cap
  • an expiry date, at most 90 days out
  • a set of scopes, for example bids:write or creatives:read

The token is the agent's. The money, the account and the responsibility stay with the human.

The twelve tools

Tool Scope Writes? What it does
get_account account:read no Company, mandate, scopes of the current grant
list_my_bids bids:read no The advertiser's bids for one auction day
place_bid bids:write yes A sealed bid on a future day, with an idempotency key
increase_bid bids:write yes Raise an active bid
list_creatives creatives:read no Every creative and its moderation state
get_creative creatives:read no One creative by id
upload_creative creatives:write yes Fetch an https image, create a draft with copy
update_creative creatives:write yes Edit the copy of a draft
submit_creative creatives:write yes Send a complete draft to moderation
list_invoices invoices:read no Invoices for won auctions
sandbox_place_bid bids:write no Check a bid against the mandate, spend nothing
sandbox_upload_creative creatives:write no Run the creative preflight, store nothing

The two sandbox twins take the same arguments as the real tools and never write. An agent should call them first.

Consent and the mandate

The consent flow is standard OAuth 2.1 with PKCE. The one nowyourlink-specific step is the mandate, which the human sets on the consent page. The agent never sees a form; it receives a token whose limits are already fixed.

Sequence diagram: agent, api.nowyourlink.com, nowyourlink.com consent page, and the advertiser exchange an authorization request, a signed hand-off, a mandate, a one-time approval and finally an access token

Three details are worth knowing:

  1. Client ID Metadata Documents. The agent's client_id is an https URL that serves its own metadata. No registration form, no shared secret.
  2. One-time hand-off. The consent page talks to the API over a signed, single-use hand-off. A replayed approval is refused.
  3. Eligibility is checked at consent. An account with no payment method, a blocked VAT status or an open verification cannot grant bids:write. The refusal names the missing step and links to it.

The mandate travels inside the token. The API re-validates it on every write, and the refresh token cannot outlive valid_until.

Reserve, then compensate

A daily cap is easy to state and easy to get wrong. Two bids sent at the same moment must not both slip under the cap. nowyourlink reserves the amount before it talks to the auction engine.

Flowchart: an idempotency-key check, a guarded reservation against the daily cap, the auction engine call, and three outcomes: accepted keeps the reservation, refused releases it, unknown keeps it and logs

  • The reservation is one guarded database upsert. If the new total would exceed the cap, the row does not change and the call answers 403 agent.mandate-exceeded with limit, used and requested.
  • The idempotency key is claimed in the same transaction. A retry with the same key replays the earlier answer and reserves nothing.
  • If the engine refuses the bid, the reservation is released. If the answer is lost, the reservation stays. Money is never under-counted.

Creative preflight with Jev

Before a creative reaches human moderation, submit_creative runs a short preflight through TypeSafe's Jev model. Four typed questions: deceptive claim, prohibited category, prompt injection in the copy, and an overall risk score. A clear block answers 422 agent.creative-preflight-blocked with the signals. Everything else goes to the moderators as before. When the model is unreachable, the preflight steps aside and the draft still submits.

sandbox_upload_creative runs the same preflight without storing anything, so an agent can fix copy before it uploads an image.

How an agent finds all of this

Discovery follows the RFC 9728 and RFC 8414 chain, plus the auth.md convention.

Flowchart: protected-resource metadata points to authorization-server metadata, which carries an agent_auth block and links to auth.md and the advertiser server card

  • An unauthenticated call to api.nowyourlink.com/mcp answers 401 with a WWW-Authenticate header that names the protected-resource document and the scopes.
  • That document names the authorization server. The server's metadata carries the agent_auth block with the identity, claim and revocation endpoints.
  • auth.md is the same walkthrough in prose, for humans and agents.
  • The advertiser server card lists all twelve tools without opening a transport.

Try it

With the agent kit CLI

npm install -g nowyourlink-agent-kit
nowyourlink login            # opens the consent page, stores the token locally
nowyourlink me               # your company, mandate and scopes
nowyourlink bid --day 2026-10-01 --ad ad_123 --amount 2500

The CLI mints a fresh idempotency key per run and prints it, so a retry can reuse it.

With curl

curl -s https://api.nowyourlink.com/v1/agent/me \
  -H "Authorization: Bearer $TOKEN"

curl -s -X POST https://api.nowyourlink.com/v1/agent/bids \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: 2026-10-01-first-try" \
  -H "Content-Type: application/json" \
  -d '{"day":"2026-10-01","ad_id":"ad_123","amount_cents":2500,"currency":"EUR"}'

Over MCP

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "sandbox_place_bid",
    "arguments": {
      "day": "2026-10-01",
      "ad_id": "ad_123",
      "amount_cents": 2500,
      "idempotency_key": "2026-10-01-first-try"
    }
  }
}

Send it to https://api.nowyourlink.com/mcp with Authorization: Bearer <token>. Swap sandbox_place_bid for place_bid when the answer is accepted: true.

With a skill

npx skills add ArneFfm/nowyourlink-agent-kit --skill advertise-on-nowyourlink installs the advertise-on-nowyourlink skill, which walks an agent through account check, creative, sandbox and bid in that order.

What stays with the human

Registration, VAT validation, the payment method and identity verification happen in the browser. So does revocation: the Connected agents page lists every grant with its mandate and a revoke button. Revoking deletes the grant and its spend tally at once.

Read the contract