# ADR-022: Platform Published Resource Architecture

| Field | Value |
| --- | --- |
| Status | Accepted |
| Date | 2026-07-27 |
| Deciders | Platform Architecture |
| Supersedes | — |
| Superseded by | — |
| Amended | 2026-07-27 — Published Actions, Public Access Model, Universal Embed (additive) |

## Context

Experience Platform ([ADR-016](ADR-016.md)) and many verticals publish public surfaces (sites, pages, forms, menus, campaigns, cards, events). Payment, Communication, Short Link, QR, Analytics, CRM, and future products (Torbat Link, Torbat QR, Torbat Card, Torbat Booking, Torbat Ticket, Torbat Event, Torbat Learning) need a stable cross-service identity for those surfaces.

Without a shared abstraction, services couple to Experience (or vertical) internal table ids, force Form/Survey/Appointment to depend on Site/Page, and invent incompatible URL schemes.

Public surfaces also need extensible **actions**, **access policies**, and **embed modes** without Experience owning auth, payment, or vertical business logic.

## Decision

1. Introduce **Published Resource** as the platform-wide abstraction for anything that can be published publicly.
2. Introduce a logical **Publish Target Registry** with fields: `publish_id`, `tenant_id`, `resource_type`, `resource_id`, `canonical_url`, `publish_status`, `visibility`, `owner_service`.
3. `resource_type` is an **open string registry** — architecture must not hardcode a closed list; unlimited future types are allowed.
4. **Form**, **Survey**, and **Appointment** MUST support **standalone publishing** and MUST NOT depend on Site or Page; optional Site binding remains allowed.
5. All cross-service integrations that attach to a public surface MUST reference **`publish_id` only** — never Experience/vertical internal tables.
6. Reserve standard public URL prefixes (`/s/`, `/p/`, `/f/`, `/survey/`, `/book/`, `/menu/`, `/card/`, `/event/`, …) without implementing routing in this decision.
7. Future products (Torbat Link, QR, Card, Booking, Ticket, Event, Learning) consume Published Resources and never own Experience data.
8. Payment may attach to any Published Resource; Communication triggers, Short Link, QR, and Analytics all key off `publish_id`.
9. Introduce **Published Actions**: every Published Resource may expose zero or more actions via an open **Action Registry** (`action_key`, `display_name`, `owning_service`, `permission`, `capability`, `feature_toggle`, `supported_resource_types`). Future services register actions without modifying Experience. Contract: [published-action-registry.md](../../reference/published-action-registry.md).
10. Introduce a **Public Access Model** with an open strategy registry (Public, Private, Authenticated, Tenant Members, Role Based, Password Protected, OTP Protected, Paid Access, Invitation Only, Signed URL, Temporary Link, Expiration, Rate Limited, Geo Restricted reserved, future). Experience must never own authentication (Identity/Core), payment unlock (Payment), or membership evaluation (Loyalty). Contract: [public-access-contract.md](../../reference/public-access-contract.md).
11. Introduce **Universal Embed Architecture** with open embed types (Iframe, JavaScript SDK, Widget, Popup, Inline, Modal, Floating Button, future). Contracts cover responsive sizing, security/sandbox, origin validation, permission delegation, token passing, theming inheritance, analytics propagation. Contract: [embed-contract.md](../../reference/embed-contract.md).
12. Experience owns publishing, presentation, rendering metadata, and embed metadata; other services own business logic for their actions and domains.
13. Normative detail lives in [published-resource-architecture.md](../published-resource-architecture.md) and related contracts.
14. This ADR is **architecture/documentation only** — no schema, API, frontend, or migration is authorized by accepting this ADR alone. Implementation requires a future registered phase.
15. With decisions 1–14 documented, Experience Platform architecture is marked **ARCHITECTURE COMPLETE** (implementation remains future work).

## Consequences

### Positive

- One public identity (`publish_id`) for all present and future products
- Decouples Payment / Communication / Link / QR / Analytics from Experience internals
- Forms, surveys, appointments can ship as standalone public products
- Extensible resource types, actions, access strategies, and embed types without rewriting Experience
- Clear ownership: Experience presents; verticals execute

### Negative

- Future implementation must introduce registry storage and resolution APIs carefully
- Existing publish shells may need additive mapping to `publish_id` / actions / access / embed in later phases

### Neutral

- Experience remains owner of its aggregates ([ADR-016](ADR-016.md))
- Completed phases 11.0–11.10 are unchanged by this ADR
- Additive amendments are backward compatible with the initial Published Resource decisions

## Alternatives Considered

1. **Use Experience `page_id` / `form_id` as the global key** — rejected (couples all consumers to Experience schema; fails for non-Experience owners).
2. **Per-product public identity** — rejected (duplicates Short Link / QR / Payment attachment models).
3. **Require Site→Page hierarchy for all surfaces** — rejected (blocks standalone Form/Survey/Appointment products).
4. **Hardcode action / access / embed enums in Experience** — rejected (blocks future services from registering capabilities).
5. **Experience owns authentication for public access** — rejected (violates Identity/Core ownership).

## Related Documents

- [Published Resource Architecture](../published-resource-architecture.md)
- [Published Resource Contracts](../../reference/published-resource-contracts.md)
- [Published Action Registry](../../reference/published-action-registry.md)
- [Public Access Contract](../../reference/public-access-contract.md)
- [Embed Contract](../../reference/embed-contract.md)
- [ADR-016](ADR-016.md) · [ADR-012](ADR-012.md) · [ADR-020](ADR-020.md) · [ADR-002](ADR-002.md) · [ADR-004](ADR-004.md) · [ADR-011](ADR-011.md)
- [Module Boundaries](../module-boundaries.md)
- [Experience Roadmap](../../experience-roadmap.md)
