Palace BeaconDevelopersMarkdown guide

This banner is a live showcase of Palace Beacon’s ad technology.

Integrate Palace Beacon

Record interest evidence from your app’s agent. Read permitted profiles when choosing ads for separate placements.

Developer preview. The profile gateway runs locally; public account and key provisioning is still being built.

Installing a website ad placement by hand? Open the developer console to create a test placement, copy two lines of code and watch the install check pass.

Give your coding agent the integration instructions, or follow the guide below.

Read the installation prompt

Install Beacon

Copy the installation prompt above into the coding agent working in your application. It describes the signal hooks, permission bindings and separate ad placement to implement.

For the examples below, save the server adapter, taxonomy.json and client.mjs in the same backend directory. Set BEACON_API_ORIGIN, BEACON_API_KEY and BEACON_SUBJECT from your local provisioner. The subject must match a grant for this app; in production, resolve it from the authenticated session.

client.mjs · shared backend setup
import { BeaconAgentClient } from './agent.mjs';

export function required(name) {
  const value = process.env[name];
  if (!value) throw new Error('Set ' + name + ' on the backend.');
  return value;
}

export const subject = required('BEACON_SUBJECT');
export const beacon = new BeaconAgentClient({
  origin: required('BEACON_API_ORIGIN'),
  apiKey: required('BEACON_API_KEY'),
});

Run the local gateway

  1. Use a checkout of the Beacon repository. The gateway is a local development service.
  2. Run the integration check, build the profile process, then start the gateway with a private configuration and state path.
  3. In the configuration, register an agent-enabled app and a random server key of at least 32 characters. Bind its subject and profile in an AgentGrant with the required purposes. The fixture provisioner supplies these bindings; it does not verify production account ownership.
Terminal
npm run agent:check
cargo build --locked --offline --bin beacon-profiles
node ops/agent-gateway.mjs /private/config.json /private/profiles.json

The gateway listens on 127.0.0.1:8958. Keep its configuration and state outside public assets.

Record an interest event

Submit evidence from a user statement or action through the host’s durable backend queue. Return the agent’s response without waiting for Beacon.

This local fixture records tennis affinity. Set BEACON_EVENT_ID to a stable ID and BEACON_GRANT_ID to its provisioned grant. Use the same event ID when retrying; changing content under that ID is rejected.

record-interest.mjs · synthetic local fixture
import { beacon, subject, required } from './client.mjs';

const receipt = await beacon.track({
  schemaVersion: 1,
  id: required('BEACON_EVENT_ID'),
  grantId: required('BEACON_GRANT_ID'),
  appSubjectId: subject,
  conversationId: 'local-tennis-example',
  topicId: 'sports.tennis',
  kind: 'affinity',
  origin: 'user_statement',
  about: 'user',
  confidence: 0.9,
  observedAtMs: Date.now(),
  expiresAtMs: Date.now() + 30 * 24 * 60 * 60 * 1000,
  evidenceRef: 'local-example-turn-1',
});
console.log(JSON.stringify(receipt, null, 2));

In a host integration, bind identity and grant in trusted code. Replace the fixture conversation and evidence reference with opaque host references. Send structured evidence rather than conversation text.

A statement about another person, a hypothetical question or agent-authored text cannot establish a user interest. An ad response retains its own origin and lower weighting.

Find an interest category

Search eligible category labels, IDs and source paths. Use the returned ID in an event.

998 eligible categories

The catalog has 1,013 IDs, with 998 eligible for profiles. It includes 154 Google affinity and 779 in-market source paths. Catalog coverage does not measure recognition accuracy; imported entries have no automatic keyword classifier. Authenticated Meta IDs are not available.

Affinity lasts at most 90 days and intent 14 days. Scores decay with half-lives of 21 and 3 days. Confidence is a host assertion rather than a calibrated probability.

Read an interest profile

Fetch an ads profile from your backend when choosing an ad for a separate placement.

read-profile.mjs · uses client.mjs from Installation
import { beacon, subject } from './client.mjs';

const profile = await beacon.profile(subject, 'ads');
console.log(JSON.stringify(profile, null, 2));

Example response

Synthetic tennis fixture. Scores decay; your values depend on the permitted evidence.

JSON · AgentProfile
{
  "taxonomyVersion": "beacon-interests-3",
  "asOfMs": 1794002400000,
  "purpose": "ads",
  "topics": [
    {
      "topicId": "sports.tennis",
      "label": "Tennis",
      "kind": "affinity",
      "score": 0.9,
      "evidenceCount": 1,
      "supportingApps": 1,
      "lastObservedAtMs": 1794002400000,
      "expiresAtMs": 1796594400000
    }
  ]
}

Permissions and fallback

ads
Requires the grant’s ads purpose; use the topics in a separate placement.
personalization
Requires the separate personalization purpose.
Cross-app evidence
Requires a trusted shared identity and network permission on both source and receiving grants.

A successful empty profile has topics: []. Use your contextual fallback when there is no permitted, unexpired evidence.

A missing or expired subject grant is a domain rejection (400). An invalid app key returns 401. Keep the host usable if the request fails.

Retract evidence or revoke a grant

Use the backend client and app subject from Installation. Each operation below is a separate action.

Retract one event

Set BEACON_EVENT_ID to the source event to remove. Its tombstone prevents a retry from restoring the evidence.

retract-event.mjs
import { beacon, subject, required } from './client.mjs';

const receipt = await beacon.retract(subject, required('BEACON_EVENT_ID'));
console.log(JSON.stringify(receipt, null, 2));

Revoke one grant

Set BEACON_GRANT_ID to the app grant to withdraw. Revocation erases its source events and survives restart.

revoke-grant.mjs
import { beacon, subject, required } from './client.mjs';

const receipt = await beacon.revoke(subject, required('BEACON_GRANT_ID'));
console.log(JSON.stringify(receipt, null, 2));

Match a campus location

The matcher includes Samsung Digital City in Suwon and Stanford’s main campus. An IP point yields area candidates. A permissioned device estimate can yield a location match when its reported accuracy circle plus a 25 m guard fits inside the mapped perimeter.

Test the matcher

Optional browser permission. Coordinates are processed on this device and are not uploaded or saved.

Test an office-area scenario

Sourced public points, synthetic visits. The 250 m proximity window is a configurable search area, not measured accuracy or proof of affiliation.

Coordinate source

IP accuracy is unknown here. Finer provider coordinates are used internally for candidate matching; public coordinates and weather cache cells remain coarse. Stale, denied and missing-accuracy readings stay unavailable. Boundary overlaps remain candidates.

Browser Geolocation requires an explicit user action and permission. The demo processes coordinates locally without uploading or retaining them. A campus estimate does not establish employment or enrollment. The community-mapped perimeters are not surveyed boundaries.

Network registration is separate: AS32 maps to Stanford University and AS6619 to Samsung SDS, including possible customer or remote traffic. These signals do not establish a person’s affiliation.

API reference

Use a backend bearer key for every /v1 request. The key determines the app. POST bodies must be JSON and at most 64 KiB. The adapter requires HTTPS outside loopback and has a five-second timeout. /api/context is the website’s separate, unauthenticated environment endpoint.

GET /api/context

Available request observations; enrichment is optional

enrich
1 · optional query parameter

GET /v1/taxonomy

Find up to 20 eligible categories

q
string · required query parameter

GET /v1/profile

Read a purpose-scoped interest profile

subject
string · required query parameter
purpose
ads | personalization · required query parameter

POST /v1/retract

Retract an event and retain its replay tombstone

JSON body: subject, eventId

POST /v1/revoke

Revoke a grant and erase its source events

JSON body: subject, grantId

Event fields · all required
FieldType / values
schemaVersion1
idstring
grantIdstring
appSubjectIdstring
conversationIdstring
topicIdstring
kindAgentInterestKind
originuser_statement | user_action | ad_response | agent_text
aboutuser | other | hypothetical
confidencenumber
observedAtMsinteger
expiresAtMsinteger
evidenceRefstring

Gateway success responses use HTTP 200. Errors contain an error string: 400 for invalid input or a domain rejection, 401 for an invalid key, 404 for an unknown route, 413 for an oversized body, 415 for a non-JSON POST, and 429 at local capacity. Queue transient retries with the same event ID; invalid grants require correction.

Verify the integration

  1. Record one supported interest and confirm it appears in a permitted profile. Replay the same event without increasing its count.
  2. Read another, unlinked user’s profile and confirm it contains no shared evidence. Reject hypothetical, other-person and agent-authored claims.
  3. Retract the event, then verify it disappears and cannot return through replay. Test grant revocation independently, including a process restart.
  4. Delay or fail the Beacon request while continuing the host conversation and navigation. Prove durable replay after a host restart. Keep credentials out of bundles, telemetry and model context.

The local engine is single-writer and acknowledges atomic private-file replacement. Its limit is 100,000 events plus tombstones. Abrupt termination can require stale-lock recovery after confirming the writer is gone. Production provisioning, retention compaction, extraction evaluation and paid delivery remain implementation work.