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:
- Authentication for key formats, headers, and rotation.
- Endpoints for the full list of ten routes.
- Webhook events for the sixteen event types.
- Error codes for the canonical 4xx/5xx envelope.
- Rate limits for the per-key request budgets.
- Deny list for endpoints and events that are NOT part of the public surface.
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
- Interactive OpenAPI explorer:
https://valara.cloud/api/openapi.json(legacy alias:https://dash.jacoballenmedia.com/api/openapi.json) - Postman collection: postman/README.md
- Email support: support@valara.cloud
- Status page:
https://status.valara.cloud