# 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).