Skip to main content

Media Agency API Overview

The Valara Media Agency API is the public programmatic surface that lets accredited media agencies and brokers drive the same listing, media, and scheduling workflows the dashboard UI ships today. Ten core endpoints cover the full listing lifecycle, sixteen webhook event types stream change notifications back to your side, and a single agency-scoped API key is all you need to go live.

This page is the integrator's table of contents. Read it end to end before wiring the first request; subsequent pages drill into authentication, webhooks, errors, rate limits, and the deny list.

Base URL

All requests target the /api/v1/ prefix on either of the two production hosts. Both hosts serve live traffic per CLAUDE.md rule 10a; examples in these docs use the primary host.

https://valara.cloud/api/v1/             (primary)
https://dash.jacoballenmedia.com/api/v1/ (legacy alias; scheduled for retirement)

The OpenAPI specification is published at https://valara.cloud/api/openapi.json (and mirrored at https://dash.jacoballenmedia.com/api/openapi.json). Machine-generated SDKs should consume the primary URL; the legacy alias is kept for integrators who coded against dash.jacoballenmedia.com before the migration and have not yet cut over.

Who this API is for

The public API is gated by API keys minted from inside a media_agency or broker dashboard account. A single key is scoped to one agency; the platform auto-enforces cross-agency isolation on every request, so a key issued under one media agency can never read or mutate another agency's listings, users, or invoices. Platform identities (super_admin, admin) can mint keys on behalf of any agency for support use, but the resulting key behaves identically and is bound to the agency hierarchy it was minted under.

10-minute quickstart

This walkthrough assumes you already have a dashboard account with the media_agency or broker role. If you do not, contact support@valara.cloud before continuing.

1. Mint an API key (1 minute)

From the dashboard, navigate to Settings > API Keys and click Generate new key. Keys are shown exactly once; copy the plaintext value (format ma_live_ak_<ULID>) into a secrets manager. The server only stores a bcrypt hash, so a lost key cannot be recovered: rotate instead.

2. Create a listing (3 minutes)

POST /api/v1/listings accepts the address and the assigned agent and returns the new listing_number (the 6-8 character slug used everywhere else in the API). Every mutating request MUST include an X-Idempotency-Key header (any UUID v4 is fine) so replays are safe.

curl -X POST "https://valara.cloud/api/v1/listings" \
-H "Authorization: Bearer $VALARA_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"address": "123 Main St, Portland, OR 97201",
"owner_user_id": "user_01HXAGENCYOWNER",
"agent_one_id": "user_01HXAGENTONE"
}'

The response carries the new listing number, full canonical URL at https://valara.cloud/property/<listing_number>, and the initial status: "Coming Soon".

3. Register a webhook endpoint (2 minutes)

POST /api/v1/webhooks/endpoints accepts an HTTPS URL you control and returns the signing secret exactly once. The secret is used to verify every delivered payload. Subscribe to the event types you care about; the full catalog is at GET /api/v1/webhooks/events and mirrored in webhook events.

curl -X POST "https://valara.cloud/api/v1/webhooks/endpoints" \
-H "Authorization: Bearer $VALARA_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"url": "https://your-service.example.com/valara/webhooks",
"event_types": [
"listing.created",
"listing.status_changed",
"media.delivery.completed"
]
}'

4. Receive the first event (2 minutes)

Within seconds of step 2 completing, the registered endpoint receives a listing.created delivery. Payloads carry a signed envelope:

{
"event_id": "evt_01JXYZTESTEVTID",
"event_type": "listing.created",
"api_version": "2026-05-29",
"timestamp": 1745339401,
"nonce": "01HXNONCE0000000000000000",
"data": { "listing_number": "ABC123", "status": "Coming Soon" }
}

The signature is in the X-Valara-Signature header as sha256=<hex>; the recipe is documented in the webhook overview. Reject any delivery whose timestamp is more than 5 minutes off your wall clock or whose nonce you have seen in the last 10 minutes.

5. Keep going

Cross-reference:

Conceptual model

Agency scope

Every API key is bound to exactly one media agency (or broker). Every response is pre-filtered to the caller's agency hierarchy, so a listing query returns only listings owned by the agency (or their downstream teams, agents, assistants, and viewers). A cross-agency read does not return 403: it returns 404, which prevents id enumeration across the tenant boundary.

Cursor pagination

List endpoints (GET /api/v1/listings, GET /api/v1/users, GET /api/v1/listings/{id}/orders) return a next_cursor field. Pass it back on the next request as ?cursor=... to fetch the following page; the empty string signals the end of the collection. Page size defaults to 50 and is capped at 200 via the ?limit= query parameter.

Idempotency

Every write (POST, PATCH) MUST carry an X-Idempotency-Key header. The server caches the response for 24 hours, keyed on (agency_id, idempotency_key). Retries with the same key return the cached response and flag it with X-Idempotent-Replay: true. Retries with the same key but a different body return 409 idempotency_key_conflict.

Versioning

The URL path prefix (/api/v1/) is the major version. Within a major version we add fields and event types additively; removing or renaming a field is a breaking change that ships only under /api/v2/. The OpenAPI spec's info.version tracks the semantic version of the additive surface so integrators can detect new capabilities without reading release notes.

Versioned event payloads

Each webhook event envelope carries an api_version string. The data shape for a given (event_type, api_version) pair is locked by contract tests; adding fields is allowed, removing or renaming fields bumps the event type's api_version. See webhook events for the current version of each event.

Help and support