# Real Signal — public response schemas
version 1.0.0 · JSON Schema draft 2020-12 · additive changes are not breaking; breaking changes bump this version with a note in [/mcp/CHANGELOG.md](https://real-signal.ai/mcp/CHANGELOG.md).
every response also carries a `_meta` envelope: `{ source, source_url, license: "CC BY-NC-ND 4.0", license_url, attribution_required: true, endpoint, computed_at }`. attribution is required in any UI surfacing this data.
real example payloads for each schema: [/docs/examples](https://real-signal.ai/docs/examples).
## pocket-now — GET /api/pocket-live?pocket=<id>
the realtime pocket snapshot behind /now/:pocket and /pocket/:id/live.
```json
{
"$id": "https://real-signal.ai/docs/schemas#pocket-now",
"type": "object",
"required": ["pocket", "generated_at", "atmosphere_latest", "stats"],
"properties": {
"pocket": { "type": "string", "description": "pocket id, e.g. cluny" },
"generated_at": { "type": "string", "format": "date-time" },
"projection_version": { "type": "string" },
"atmosphere_latest": {
"type": ["object", "null"],
"description": "latest 15-min atmosphere reading; null when the pocket has no reading yet",
"properties": {
"primary_state": { "enum": ["calm", "rushed", "social", "working", "quiet"] },
"calm": { "type": "number", "minimum": 0, "maximum": 1 },
"stress": { "type": "number", "minimum": 0, "maximum": 1 },
"anomaly_flag": { "type": "boolean" },
"captured_at": { "type": "string", "format": "date-time" }
}
},
"packages_recent": { "type": "array", "items": { "type": "object" } },
"predictions_recent": { "type": "array", "items": { "type": "object" } },
"sustainability_recent": { "type": "array", "items": { "type": "object" } },
"signal_tier": { "type": ["object", "null"] },
"resonance": { "type": ["object", "null"] },
"stats": {
"type": "object",
"properties": {
"atmosphere_24h": { "type": "integer" },
"packages_7d": { "type": "integer" },
"predictions_revealed_7d": { "type": "integer" },
"recent_signal_count_60m": { "type": "integer" }
}
}
}
}
```
## live-experiences — GET /api/live-experiences?pocket=<id>
user agent discovery packets: merchant-confirmed, live, in-window experience commitments only. an empty list is the honest state — nothing appears before a merchant accepts.
```json
{
"$id": "https://real-signal.ai/docs/schemas#live-experiences",
"type": "object",
"required": ["pocket_id", "count", "experiences", "generated_at"],
"properties": {
"pocket_id": { "type": "string" },
"count": { "type": "integer", "minimum": 0 },
"experiences": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "why", "value", "window", "window_remaining_minutes", "expires_at"],
"properties": {
"id": { "type": "string" },
"experience_name": { "type": "string" },
"outlet": { "type": ["object", "null"], "properties": { "id": { "type": "string" }, "name": { "type": ["string", "null"] } } },
"why": {
"type": "object",
"required": ["why_now", "why_merchant", "why_users", "why_pocket"],
"properties": {
"why_now": { "type": "string" },
"why_merchant": { "type": "string" },
"why_users": { "type": "string" },
"why_pocket": { "type": "string" }
}
},
"value": {
"type": "object",
"properties": {
"shape": { "enum": ["discount", "bundle", "upgrade", "access", "add_on", null] },
"line": { "type": ["string", "null"] },
"offer_price_sgd": { "type": ["number", "null"] },
"original_price_sgd": { "type": ["number", "null"] },
"inventory_limit": { "type": ["integer", "null"] }
}
},
"window": {
"type": "object",
"properties": {
"start_at": { "type": ["string", "null"], "format": "date-time" },
"end_at": { "type": "string", "format": "date-time" },
"window_sgt": { "type": ["string", "null"] }
}
},
"window_remaining_minutes": { "type": "integer", "minimum": 1 },
"merchant_ready": { "const": true },
"receipts": { "type": "object", "properties": { "line": { "type": ["string", "null"] } } },
"expires_at": { "type": "string", "format": "date-time" }
}
}
},
"atmosphere": { "type": ["object", "null"] },
"confidence": { "type": ["number", "null"] },
"confidence_source": { "type": ["string", "null"] },
"generated_at": { "type": "string", "format": "date-time" }
}
}
```
## moment-stories — MCP tool get_live_moment_story
the four-clause live moment story (dual discovery stage 2). served via the MCP server at `POST /api/mcp` (JSON-RPC `tools/call`); stories die with their moment (`expires_at`).
```json
{
"$id": "https://real-signal.ai/docs/schemas#moment-story",
"type": "object",
"required": ["pocket_id", "story"],
"properties": {
"pocket_id": { "type": "string" },
"story": {
"type": ["object", "null"],
"description": "null when no unexpired story exists — the agent does not narrate weak moments",
"required": ["why_now", "why_merchant", "why_users", "why_pocket", "expires_at"],
"properties": {
"id": { "type": "string" },
"why_now": { "type": "string" },
"why_merchant": { "type": "string" },
"why_users": { "type": "string" },
"why_pocket": { "type": "string" },
"band": { "enum": ["forming", "high_resonance", "peak", "decaying"] },
"mqs": { "type": "number" },
"created_at": { "type": "string", "format": "date-time" },
"expires_at": { "type": "string", "format": "date-time" }
}
}
}
}
```
## notification-candidates — GET /api/notification-candidates
the dispatch layer's transparency surface: gate-passed, un-dispatched discovery packets. aggregate-only — no addresses, no hashes, no user ids; read path only.
```json
{
"$id": "https://real-signal.ai/docs/schemas#notification-candidates",
"type": "object",
"required": ["window", "candidates", "counts"],
"properties": {
"window": { "enum": ["1h", "1d", "7d"] },
"candidates": {
"type": "array",
"items": {
"type": "object",
"required": ["packet_type", "pocket_id", "status", "reason", "created_at"],
"properties": {
"packet_type": { "enum": ["merchant_moment_line", "customer_moment_radar"] },
"pocket_id": { "type": "string" },
"status": { "enum": ["candidate", "dry_run"] },
"channel": { "enum": ["email", "sms", "whatsapp", "webpush", null] },
"reason": {
"type": "object",
"description": "the six answers every packet must carry; a packet missing any is never dispatched",
"required": ["why_now", "why_me", "whats_happening", "what_value", "what_can_i_do", "how_long"],
"properties": {
"why_now": { "type": ["string", "null"] },
"why_me": { "type": ["string", "null"] },
"whats_happening": { "type": ["string", "null"] },
"what_value": { "type": ["string", "null"] },
"what_can_i_do": { "type": ["string", "null"] },
"how_long": { "type": ["string", "null"] }
}
},
"gate": { "type": "object", "properties": { "pass": { "type": "boolean" }, "note": { "type": ["string", "null"] } } },
"created_at": { "type": "string", "format": "date-time" }
}
}
},
"counts": {
"type": "object",
"properties": {
"candidate": { "type": "integer" },
"dry_run": { "type": "integer" },
"by_pocket": { "type": "array", "items": { "type": "object", "properties": { "pocket_id": { "type": "string" }, "count": { "type": "integer" } } } }
}
}
}
}
```
---
more surfaces: the full endpoint catalogue lives at [/api](https://real-signal.ai/api); live vitals at [/status](https://real-signal.ai/status); the MCP tool contract at [/mcp/reference.md](https://real-signal.ai/mcp/reference.md).