---
name: applicaster-cloud-events
description: "applicaster-cloud-events: use when implementing a new Applicaster Cloud Events receiver, auditing or hardening an existing one, adding a new documented or extension event type to a receiver, or resolving async/idempotency/ordering behavior for event handlers."
---

# applicaster-cloud-events

Add or audit a compliant, secure Applicaster Cloud Events receiver in a Pipes/library project. The **contract** is the resolved agreement between the live Applicaster protocol, the target project, its provider/service integrations, and Zapp. Establish and obtain approval for it before editing any code, whether the work is new implementation or an audit-driven change.

## Scope

- Use this skill for implementing a new Cloud Events receiver, auditing/hardening an existing one, adding a documented or repository-extension event type to a receiver, or resolving async/idempotency/ordering behavior.
- Covers every event in the live Applicaster Cloud Events protocol: `video.started`, `video.stopped`, `video.progress`, `video.paused`, `video.interval`, `favorites.added`, `favorites.removed`, `pin.v1`, `pin.recovery.requested.v1`, and the generic `com.applicaster.event.received.v1` receipt envelope. Also covers repository/customer-only extension events (e.g. `SELECT_PROFILE`, `REMOVE_DEVICE`, `AGE_RATING_SELECTED`, `TOGGLE_USER_PREFERENCE`, `POST_DATA`) as extensions, not part of the official protocol.
- For Continue Watching/Favorites feed-lifecycle work specifically (synced feed shape, resume extensions, favorites GET), use [applicaster-continue-watching-favorites](../applicaster-continue-watching-favorites/SKILL.md) instead; this skill governs the general event-receiver boundary, not the personalized-feed contract.
- Structured JSON CloudEvents only. Binary-mode (header-encoded) CloudEvents are out of scope; unsupported content types must be rejected, not silently accepted.
- Adapt to the target project's language, framework, package manager, configuration, tests, and deployment conventions. No shared runtime package is introduced; recommend the target project centralize its own event-type constants and response builders.
- For a stateless HTTP-boundary check of a receiver's request/response contract, use the skill-local general Cloud Events E2E tool at `tools/cloud-events-e2e/`. It is not the personalized-feed lifecycle tool (`continue-watching-favorites-e2e`), and it does not replace focused route/unit tests.

## Non-Negotiable Gates

1. Before proposing an implementation contract or editing files — for new work or an audit-driven change alike — present the contract described in step 3 and receive explicit approval. Discovery may proceed freely before this gate.
2. Every route validates the CloudEvents envelope shape, checks the event type against an explicit supported-event allowlist, validates event-specific payload fields, and enforces a body size limit — all before calling any adapter or service.
3. Response policy is per-event, not a single global rule: malformed/unsupported events return sanitized `400`; missing/invalid identity returns `401`; upstream/provider failures return sanitized `502`/`503`; success responses follow the documented contract for that specific event. Do not impose one response body or media type across all routes.
4. Bind `data.userIdentifier` to the authenticated request/context principal when both exist, except where an event's documented contract defines different identity requirements (PIN and PIN-recovery events may legitimately differ); do not silently override a documented per-event exception with the blanket identity-binding rule.
5. Never log raw event payloads, tokens, or user identifiers. Logs carry only operation, route, event type, event ID, and status class.
6. An audit's primary artifact is the standardized findings report in [audit-checklist.md](references/audit-checklist.md); a fresh implementation contract is only produced afterward if changes are then approved.
7. Every final report for a deployable integration contains a `## Zapp Configuration` section (or an explicit "not applicable" for code-only/mock-only audits).

## 1. Establish facts

1. Fetch the current `docs.applicaster.com` Cloud Events documentation (and any pages it links to that are directly relevant, e.g. PIN code) before reading provider material. Read [applicaster-contract.md](references/applicaster-contract.md) while doing so; it is a fallback snapshot only.
2. Determine the branch: new receiver, audit of an existing receiver, new event type on an existing receiver, or an async/idempotency/ordering question on existing code. An audit and a new-event addition can combine — an audit may surface the need to add a documented event that was previously unsupported.
3. Inspect the target project's existing event route(s), dispatch logic, validation, identity handling, logging, response builders, tests, and any downstream provider/service adapters. For an audit, read [audit-checklist.md](references/audit-checklist.md) and apply every check item against the current code, citing file/route evidence for each.
4. Determine facts without asking: framework, test/build commands, current route/dispatch conventions, protected-route identity source, existing event-type constants, provider timeout conventions, and whether the receiver is synchronous or already async (Location header, queued processing).
5. Read [patterns.md](references/patterns.md) for portable dispatch, identity, idempotency, ordering, and logging shapes. Apply an invariant only when the target's architecture and documented provider support it.

Completion: every implementation constraint, or every audit finding, is evidenced by current live documentation and the actual target code, not assumed.

## 2. Resolve developer decisions

Present a concise, sourced fact summary before the first question. Ask only for decisions facts cannot settle, one at a time, each with the discovered constraint, a recommended answer, and the consequence of choosing differently; wait for the reply before continuing.

Resolve these when facts leave a material ambiguity:

1. Confirm the exact set of event types in scope for this piece of work (documented, extension, or both) and which are net-new versus already implemented.
2. Confirm the authentication model per event: which events bind `data.userIdentifier` to a context/token principal, and which (if any, e.g. PIN events) have a documented exception. Missing or invalid identity is `401`.
3. Confirm the provider/service each event dispatches to, and whether that call is synchronous or asynchronous. For asynchronous flows, confirm acknowledgement timing (`201` vs `202`), the deduplication key (the Cloud Event `id` when the provider durably supports it; otherwise the provider's own idempotency mechanism, documented explicitly), and ordering policy (trusted provider version/timestamp when available; otherwise last-received-write-wins, documented as a limitation).
4. Confirm the body size limit and runtime validation approach (Nest validation, Zod, AJV, Joi, or the project's existing convention) — do not mandate a specific library.
5. Confirm which events are intentionally unauthenticated, and require that decision to be documented rather than left implicit.
6. For an audit, confirm which findings the developer wants remediated now versus filed as follow-up; only approved remediations proceed to an implementation contract.

If live protocol documentation cannot be fetched, state that the bundled snapshot may be stale and ask whether to proceed from it before using [applicaster-contract.md](references/applicaster-contract.md). Do not silently substitute it.

Completion: every applicable decision has a recorded developer answer, and only genuinely unresolved decisions were asked.

## 3. Confirm before edits

Present a concise contract containing:

- The event types in scope, distinguishing documented protocol events from repository extensions.
- The authentication model, including any per-event identity exceptions.
- The validation rules: envelope shape, allowlist, per-event payload checks, body size limit.
- The success and error response for each event in scope, including status code, body shape, and media type.
- The provider/service mapping for each event, and the synchronous/asynchronous treatment (ack timing, dedup key, ordering policy) for any asynchronous flow.
- The affected routes/files, the tests to be added or updated, and any configuration/secrets required.
- For an audit: the findings being remediated in this pass (severity, location, remediation) versus those filed as follow-up only.

Wait for explicit approval. If a material decision changes later, ask one recommended question and refresh the contract.

Completion: the developer has approved every non-discoverable choice affecting events in scope, identity, validation, responses, provider mapping, and tests.

## 4. Implement the contract

1. Validate body size, the CloudEvents envelope shape, the event type against the explicit allowlist, and event-specific payload fields before calling any adapter or service. Reject unsupported content types (binary-mode CloudEvents, wrong media type) with `400`/`415`.
2. Implement and test each route's documented success response per event; do not force one shared body or media type. Return `400` for malformed/unsupported events, `401` for identity failures (respecting any documented per-event exception), and sanitized `502`/`503` for provider/service failures.
3. For asynchronous flows, implement the agreed ack timing, use the Cloud Event `id` as the dedup key when durably supported (otherwise the documented provider mechanism), and apply the agreed ordering policy. State plainly in code comments/tests that delivery is at-least-once and handlers must be idempotent.
4. Centralize the project's own event-type constants and response builders per the target's existing convention (mirroring `libs/utils/src/cloud-events.ts`-style centralization) without introducing a shared cross-project package.
5. Ensure no route logs raw payloads, tokens, or identifiers; logs carry only operation, route, event type, event ID, and status class.
6. Update the library's native integration surface: endpoint manifest when present, README/config documentation, and sanitized observability. Make only the direct fixes needed for the approved contract; report unrelated audit findings separately rather than fixing them unapproved.

Read [acceptance-matrix.md](references/acceptance-matrix.md) before writing or changing tests. For audit work, keep [audit-checklist.md](references/audit-checklist.md) open throughout and update the findings report as items are remediated.

Completion: every implemented or remediated route uses the approved allowlist, identity boundary, response policy, and provider mapping, matching the contract exactly.

## 5. Verify and hand off

1. Run the target project's relevant formatter/typecheck and focused tests during implementation, then its full test suite before completion.
2. Run every acceptance-matrix row applicable to the events in scope. When local endpoint URLs, event fixtures, and identity setup are available, run the skill-local general Cloud Events E2E tool (`tools/cloud-events-e2e/`) as a complementary HTTP-boundary check; otherwise report that command as blocked and rely on the route/unit tests as verification evidence.
3. Include a `## Zapp Configuration` section in the final handoff for deployable integrations: receiver URL, relevant plugin/event configuration, auth/context requirements, and validation steps — or explicit "not applicable" for code-only/mock-only audits.
4. For an audit, deliver the standardized findings report (severity, location, violated contract, evidence, risk, remediation) as the primary artifact; note which findings were remediated in this pass versus filed as follow-up.
5. Report files changed, verification commands/results, the developer's recorded decisions, the async ack/dedup/ordering policy (if applicable), and any remaining provider or deployment prerequisite.

Completion: automated checks pass, any skipped E2E run is identified with its blocker, the findings report or implementation summary is complete, and the developer has the exact Zapp configuration values or an explicit "not applicable".
