Build on Urba without scraping dashboards.
Use scoped organizer API keys to read events, attendees, orders, and webhook delivery logs. The API is designed for server-side integrations, stable contracts, cursor pagination, and clear failure modes.
export URBA_API_TOKEN="urb_live_your_prefix_your_secret"
curl -sS https://urba.now/api/developer/v1/me \
-H "Authorization: Bearer $URBA_API_TOKEN" \
-H "Accept: application/json"Publish events anywhere
Sync Urba events to a venue site, artist page, CRM, newsletter builder, or analytics warehouse.
Know who is coming
Pull attendee names, emails, ticket tiers, and check-in state by event, by bounded organizer window, or exact email.
Reconcile money cleanly
Use order fields aligned to grossSales, netSales, organizerTaxCollected, and takeHome.
React without polling
Subscribe to webhooks for event, order, refund, dispute, attendee, and check-in changes.
Quickstart
Create a scoped key, verify it, then pull organizer data with copy-pasteable requests.
Create a key
Open an organizer profile, go to Developers, choose the minimum scopes, and save the token once.
Verify /me
Call /me from your server and store the organizer ID returned by the API.
Sync with cursors
Use limit and cursor for lists. Use webhooks or changed_since for incremental updates.
curl -sS "https://urba.now/api/developer/v1/organizers/{organizerId}/events?limit=25" \
-H "Authorization: Bearer $URBA_API_TOKEN"curl -sS "https://urba.now/api/developer/v1/events/{eventId}/attendees?limit=100" \
-H "Authorization: Bearer $URBA_API_TOKEN"
curl -sS "https://urba.now/api/developer/v1/events/{eventId}/[email protected]" \
-H "Authorization: Bearer $URBA_API_TOKEN"Authentication and scopes
Developer API keys are profile-scoped personal access tokens. Send them only from trusted server code.
| Scope | Allows |
|---|---|
organizers:read | Read the organizer profile attached to the token. |
events:read | List organizer events and retrieve event details. |
attendees:read | Read attendee lists, names, emails, ticket tiers, statuses, and check-in timestamps. |
orders:read | Read order summaries, buyer email, revenue fields, refund state, and payout vocabulary. |
webhooks:read | Read webhook endpoints and delivery history. |
webhooks:write | Create, update, disable, rotate, and redeliver webhook endpoints. |
Token rules
- Use
Authorization: Bearer. Query-string tokens are rejected. - Cookies do not authenticate developer API routes.
- No browser CORS is enabled for token-authenticated requests.
- Tokens stop working if the creator loses owner or manager access.
- Default expiry is 180 days. Maximum expiry is 365 days.
Pagination and filtering
Every list uses cursor pagination. Org-wide attendee and order exports must be bounded.
List conventions
- Default
limitis 50. Maximumlimitis 100. - Use
pagination.nextCursoruntilhasMoreis false. - Attendees support exact
emailfiltering. - Orders support exact
buyer_emailfiltering.
Org-wide bounds
For organizer-wide attendee and order pulls, include one of event_id, changed_since, or both starts_after and starts_before. Event-date windows cannot exceed 90 days.
curl -sS "https://urba.now/api/developer/v1/organizers/{organizerId}/attendees?changed_since=2026-06-19T00:00:00.000Z&limit=100" \
-H "Authorization: Bearer $URBA_API_TOKEN"
curl -sS "https://urba.now/api/developer/v1/organizers/{organizerId}/orders?event_id={eventId}&[email protected]" \
-H "Authorization: Bearer $URBA_API_TOKEN"const API_BASE_URL = 'https://urba.now/api/developer/v1'
const token = process.env.URBA_API_TOKEN
async function urba(path) {
const response = await fetch(`${API_BASE_URL}${path}`, {
headers: {
Authorization: `Bearer ${token}`,
Accept: 'application/json',
},
})
if (response.status === 429) {
const retryAfter = response.headers.get('retry-after')
throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`)
}
if (!response.ok) throw new Error(await response.text())
return response.json()
}
const { data: me } = await urba('/me')
const { data: events } = await urba(`/organizers/${me.organizer.id}/events`)
const firstEvent = events[0]
const attendees = firstEvent ? await urba(`/events/${firstEvent.id}/attendees`) : null
console.log({ organizer: me.organizer.name, attendees: attendees?.data.length ?? 0 })Endpoint reference
This table is generated from the same route registry that builds the OpenAPI document.
Auth
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/meInspect the current developer token and scoped organizer. | organizers:read | 1 |
Organizers
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/organizersList organizers visible to the current token. | organizers:read | 1 |
Events
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/events/{eventId}Get a single event by ID. | events:read | 1 |
| GET | /api/developer/v1/organizers/{organizerId}/eventsList events for an organizer profile. | events:read | 1 |
Attendees
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/events/{eventId}/attendeesList attendees for one event. | attendees:read | 2 |
| GET | /api/developer/v1/organizers/{organizerId}/attendeesList attendees across an organizer profile with required bounding filters. | attendees:read | 5 |
Orders
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/events/{eventId}/ordersList orders for one event. | orders:read | 2 |
| GET | /api/developer/v1/organizers/{organizerId}/ordersList orders across an organizer profile with required bounding filters. | orders:read | 5 |
Webhooks
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/organizers/{organizerId}/webhooksList webhook endpoints for an organizer profile. | webhooks:read | 1 |
| POST | /api/developer/v1/organizers/{organizerId}/webhooksCreate a webhook endpoint. | webhooks:write | 10 |
| DELETE | /api/developer/v1/organizers/{organizerId}/webhooks/{webhookId}Disable a webhook endpoint. | webhooks:write | 10 |
| GET | /api/developer/v1/organizers/{organizerId}/webhooks/{webhookId}Get a webhook endpoint. | webhooks:read | 1 |
| PATCH | /api/developer/v1/organizers/{organizerId}/webhooks/{webhookId}Update or disable a webhook endpoint. | webhooks:write | 10 |
Webhook Deliveries
| Method | Path | Scopes | Cost |
|---|---|---|---|
| GET | /api/developer/v1/organizers/{organizerId}/webhooks/{webhookId}/deliveriesList recent delivery attempts for a webhook endpoint. | webhooks:read | 1 |
| POST | /api/developer/v1/organizers/{organizerId}/webhooks/{webhookId}/deliveries/{deliveryId}/redeliverManually redeliver a recent delivery. | webhooks:write | 10 |
Webhooks
Use webhooks to keep integrations fresh without polling. Payloads are thin by default and link back to API resources.
{
"id": "evt_01J...",
"type": "order.completed",
"created": "2026-06-19T00:00:00.000Z",
"apiVersion": "v1",
"organizerId": "22222222-2222-4222-8222-222222222222",
"eventId": "33333333-3333-4333-8333-333333333333",
"data": {
"object": {
"id": "55555555-5555-4555-8555-555555555555",
"object": "order",
"apiUrl": "/api/developer/v1/events/33333333-3333-4333-8333-333333333333/orders"
}
}
}import crypto from 'node:crypto'
export function verifyUrbaWebhook(rawBody, signatureHeader, signingSecret) {
const parts = Object.fromEntries(
signatureHeader.split(',').map((part) => part.split('=', 2)),
)
const timestamp = Number(parts.t)
if (!Number.isFinite(timestamp)) return false
const now = Math.floor(Date.now() / 1000)
if (Math.abs(now - timestamp) > 300) return false
const expected = crypto
.createHmac('sha256', signingSecret)
.update(`${timestamp}.${rawBody}`)
.digest('hex')
const received = Buffer.from(parts.v1 ?? '', 'hex')
const expectedBuffer = Buffer.from(expected, 'hex')
return received.length === expectedBuffer.length && crypto.timingSafeEqual(received, expectedBuffer)
}Event types
event.createdevent.updatedevent.publishedevent.cancelledorder.completedorder.refundedorder.disputedattendee.createdattendee.updatedattendee.checked_inattendee.checked_outProduction endpoints must use HTTPS.
Deliveries time out after 10 seconds and retry for up to 72 hours with backoff.
Manual redelivery is available for 14 days. Delivery logs are retained for 30 days.
Errors and rate limits
Every response includes a requestId. Use it when debugging support issues or matching logs.
{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This token is missing the attendees:read scope.",
"requestId": "req_...",
"docUrl": "https://urba.now/developers#auth"
}
}Rate-limit contract
- 2,000 points per hour per token.
- 48,000 points per day per token.
- 100 points per minute burst.
- 5 concurrent requests per token.
- Read
X-RateLimit-Remaining,X-RateLimit-Reset, andRetry-Afterbefore retrying.
Security checklist
The API is intentionally narrow: server-side tokens, explicit scopes, no dashboard cookies, no browser CORS, and safe payloads.
Do
- Store tokens in server-side secret storage.
- Use the smallest scope set needed.
- Rotate keys on staff or vendor changes.
- Use webhooks instead of tight polling loops.
Excluded from v1
- QR secrets and scan tokens
- Stripe payment method and charge IDs
- raw IP addresses and user agents
- invite codes
- internal ranking and AI fields
Contract files
Use the OpenAPI document to generate clients, validate schemas, or import the API into Postman, Bruno, Insomnia, or your gateway tooling.
Download OpenAPI JSONIf a key leaks
- Open the organizer Developer settings and choose Compromised or Revoke for the key.
- Create a new key with the smallest scopes required.
- Move the new key into server-side secret storage and redeploy the integration.
- Review the Security tab for new IP fingerprints, high-volume attendee/order exports, 404 probing, and rate-limit continuation.
AI builder guardrails
- Put
URBA_API_TOKENin.env.localor a production secret manager. - Never use
NEXT_PUBLIC_URBA_API_TOKEN, client components, mobile apps, or browser fetches for token-authenticated calls. - Ask AI tools to build a server route or backend job that reads the token from process environment.
- Commit only placeholders such as
URBA_API_TOKEN=replace_me.
Need help with an integration?
Send the requestId, endpoint, and what you are trying to build. Do not send full API tokens in support messages.