Skip to main content

Deny List

The Media Agency Public API is a deliberate subset of the dashboard's full capability surface. This page catalogs every API trigger and webhook event type that was considered during scoping and explicitly excluded from the v1 launch (2026-05-29).

Every entry lists the specific reason we held the capability back and the supported alternative you should use today. Attempts to probe a denied URL return 404 endpoint_not_in_scope (see error codes).

Denied API triggers (16)

Listing Lifecycle

NameReasonAlternative
POST /api/v1/tours/{id}/shareSharing a tour with an external viewer grants read access across the tenant boundary; we keep that flow inside the dashboard so the consent artifact is UI-visible.Use the dashboard Share dialog at /property/{listing_number}/edit.

Media

NameReasonAlternative
POST /api/v1/tours/{id}/delivery/triggerManually re-triggering Composio cloud-storage delivery bypasses the reconciliation queue and can duplicate files at the recipient's side.Use the dashboard Deliveries tab or wait for the automatic retry schedule.
POST /api/v1/rmd/designs/{id}/exportRMD (Rapid Marketing Deployment) export runs a headless browser pipeline that is not safe for unattended API use.Trigger exports from the RMD editor UI; completed files arrive via the media.delivery events.

Scheduling

NameReasonAlternative
POST /api/v1/orders/submitOrder form submission runs the full pricing/discount evaluator and is tightly coupled to UI state. Public API integrators should use the availability endpoint and book through the dashboard.Call GET /api/v1/scheduling/availability, then book in the dashboard.
PATCH /api/v1/appointments/{id}Rescheduling / cancelling an appointment touches Google Calendar, Composio notifications, and the photographer's shift board - the safe orchestration lives in the UI.Reschedule in the dashboard Calendar; cancel via POST /api/v1/listings/{id}/cancel.

Billing

NameReasonAlternative
POST /api/v1/invoicesCreating and publishing Square invoices requires the owner's Square connection credentials; routing that through an API key would bypass the two-layer payment separation documented in CLAUDE.md.Use the dashboard Invoicing flow; listen to the invoice.payment_made webhook for settlement.
POST /api/v1/payments/charge_ccofCharging a saved card-on-file requires PCI-scoped access that is scoped to the dashboard session.Use the dashboard Payments page; saved cards are managed by the card vault integration.
POST /api/v1/credits/purchaseCredit purchases mutate platform-level subscription state (Layer 1 per CLAUDE.md). The public API covers Layer 2 (owner-level) flows only.Purchase credits from /manage/billing in the dashboard.

Users

NameReasonAlternative
POST /api/v1/users/inviteInvitations trigger Better Auth email flows and consent records that are tied to the authenticated admin session.Invite via the dashboard Settings > Team page. Webhook user.signed_up fires when the invite is accepted.
PATCH /api/v1/users/{id}Profile writes are UI-scoped to prevent a stolen API key from silently rewriting team members' names, emails, or manager assignments.Edit users in the dashboard; read via GET /api/v1/users/{id}.
DELETE /api/v1/users/{id}Hard-deleting a user cascades across tour ownership, invoice history, and audit logs in ways that the public contract should never expose.Deactivate from the dashboard; the user.deactivated webhook fires on soft-delete.

Marketing

NameReasonAlternative
POST /api/v1/email-campaignsEmail campaign sends go through Mailjet and bear marketing/legal compliance obligations that require human review in the dashboard.Use the Communication Templates UI at /manage/communication.
POST /api/v1/cold-calls/campaignsCold-call campaigns consume Vapi minutes and route through Twilio numbers that are provisioned per-agency; the headless trigger is not part of the public contract.Create campaigns in /manage/cold-calls in the dashboard.
POST /api/v1/cold-calls/campaigns/{id}:startStarting or pausing a live dialler is an operator action that needs the dashboard's confirmation modal + audit trail.Use the Start / Pause controls in the Cold Calls tab.

Support

NameReasonAlternative
POST /api/v1/support/ticketsSupport chat is platform-internal (not per-agency) and routes to the Amanda AI assistant; exposing it over a per-agency API key would break the tenant model.Use the in-dashboard Support widget or email support@valara.cloud.

Webhooks

NameReasonAlternative
POST /api/v1/webhooks/incoming/*Incoming webhook receivers (Square, Vapi, Composio, Mailjet, etc) are authenticated by provider-specific signature schemes, not the Media Agency API key. They are not addressable by integrators.Register an outbound endpoint via POST /api/v1/webhooks/endpoints instead.

Denied webhook events (14)

Support

NameReasonAlternative
support.chat.initiatedSupport chat is platform-scoped, not per-agency, and exposing it via per-agency webhooks would leak traffic from the shared Amanda AI assistant.Use the Support widget in the dashboard; transcripts live in /manage/support.
support.ticket.escalatedSupport escalation events route to the platform's operator paging channels, not to integrators.Escalations surface in /admin/support to the on-call operator.

Media

NameReasonAlternative
media.matterport.receivedIntermediate ingestion events fire many times per tour and would flood integrator inboxes without a stable downstream action attached.Subscribe to media.delivery.completed for the single per-listing terminal event.
media.floorplan.generatedFloorplan generation fires from a long-running pipeline whose retries can double-emit; downstream consumers should wait for delivery instead.Subscribe to media.delivery.completed.
media.ai_edit.completedAI edit variants are experimental and the payload shape is not yet stable across model revisions.Wait for the 2026-09 release train for a stabilised event.
media.connection.expiredOAuth expiry events carry Composio refresh-token state that is Amanda-scoped, not agency-scoped.The dashboard re-prompts the owner to reconnect; no public action required.

Billing

NameReasonAlternative
payment.completedPer-attempt payment events carry Square Payment ids that overlap with Layer 1 platform billing; integrators should use invoice.payment_made for the terminal signal.Subscribe to invoice.payment_made for the PAID transition.
payment.failedPayment failure signals carry Square decline codes that are subject to PCI-sensitive relay rules.The dashboard surfaces retry logic directly; no public webhook.
card.expiringCard expiry notifications are personal to the owner and routed through Better Auth email flows.The owner receives an email 30 days before expiry.

Users

NameReasonAlternative
user.profile_updatedProfile update events can leak PII churn across the tenant boundary; the public API exposes hierarchy changes only.Subscribe to user.hierarchy_changed for manager reassignment.
user.password_changedAuth state changes are never surfaced to third parties; this prevents API-driven consumers from learning about credential changes they should not see.Audit log is available to admins in /admin/audit-log.

Marketing

NameReasonAlternative
email.*Email engagement events (opened, clicked, bounced, unsubscribed, complained) are Mailjet-specific and route through the Communication Templates UI.Mailjet webhooks are configurable per-agency inside /manage/communication.
call.endedVapi call lifecycle events are cold-call-campaign specific and are scoped to the dashboard Cold Calls tab.Use /manage/cold-calls to review transcripts and outcomes.
call.booking_createdBooking events originating from the Vapi cold-call agent feed the internal CRM, not public integrator workflows.Use the dashboard CRM tabs.

Security contract

  • The router registry is audited against this list in rollout/05. Adding a public endpoint whose path matches a denied entry MUST remove the entry in the same PR.
  • The webhook catalog builder filters events by public=True on the registry, and the regression test tests/contracts/test_deny_list_enforcement.py asserts that every entry above is absent from the catalog response.

Source of truth

The canonical deny list lives at dashboard/fastapi_backend/app/api/v1/deny_list.py. This page is auto-generated from that module - do NOT hand-edit. Update the Python registry and re-run node docs/scripts/generate-all.mjs; the CI drift-guard at .github/workflows/docs.yml blocks PRs whose generated output differs from disk.