# Pinned Events Developer API Documentation



This Markdown bundle contains public, non-user-specific Developer API documentation for Pinned Events.



Canonical docs index: https://pinned.events/developers/docs

OpenAPI YAML: https://pinned.events/api/developer/openapi/public-api-v1



# Pinned Events API Developer Docs

Complete documentation for API authentication, scoped API keys, publishing events into channels, handling retries safely, debugging errors, using OpenAPI, and building production-ready integrations with the Pinned Events API.

Canonical URL: https://pinned.events/developers/docs

## Documentation

- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.
- [Authentication](https://pinned.events/developers/docs/authentication): Authenticate Public API requests with bearer API keys from trusted server-side code.
- [API keys and scopes](https://pinned.events/developers/docs/api-keys-and-scopes): Understand API key lifecycle, default quickstart scopes, owner-scoped channel access, and revocation behavior.
- [Channels](https://pinned.events/developers/docs/channels): Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.
- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.
- [Rate limits](https://pinned.events/developers/docs/rate-limits): Understand 429 responses, Retry-After handling, and retry strategies for busy integrations.
- [Pagination](https://pinned.events/developers/docs/pagination): Understand pagination fields returned by list endpoints, including page, limit, totalDocs, totalPages, hasNextPage, and hasPrevPage.
- [Versioning and OpenAPI](https://pinned.events/developers/docs/versioning-and-openapi): Use the OpenAPI schema, versioned /api/public/v1 routes, and documented deprecation policy for production integrations.
- [Security](https://pinned.events/developers/docs/security-best-practices): Store keys server-side, use least privilege, rotate and revoke keys, and keep secrets out of logs.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.
- [Examples](https://pinned.events/developers/docs/examples): Copy production-oriented integration patterns for CMS, CRM, forms, spreadsheets, nightly imports, Node.js, and Python.
- [FAQ](https://pinned.events/developers/docs/faq): Quick answers to common developer and product questions about the Pinned Events API.

## Core resources

- [Developer overview](https://pinned.events/developers)
- [Interactive API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [LLM documentation index](https://pinned.events/llms.txt)
- [Full LLM documentation context](https://pinned.events/llms-full.txt)


# Getting Started with the Pinned Events API

Create an API key, find a channelId, publish your first event, and retry safely.

Canonical URL: https://pinned.events/developers/docs/getting-started

## What you will do

This guide creates a first Pinned Events API integration from an existing source system into a Pinned Events channel. You will authenticate with an API key, list channels, list supported public classifications, create an event, and use the Idempotency-Key header for duplicate-safe retries.

## Prerequisites

- A Pinned Events account with account setup completed.
- Ownership of at least one channel where the API key owner can publish events.
- An API key with the channels:read and events:create scopes.
- A channelId from GET /api/public/v1/channels.
- A public classification slug from GET /api/public/v1/classifications.
- Approved event data from a CMS, CRM, spreadsheet, partner form, internal tool, or backend job.

## Base URL

```text
https://pinned.events
```

Public API endpoints use the /api/public/v1 namespace. The interactive reference and OpenAPI schema are linked from this portal.

## Step 1: Create an API key

Go to the API key management page and create one key per integration when possible. New keys start with the quickstart scopes channels:read and events:create.

![Developer portal API keys page showing key creation and management.](https://pinned.events/developers/screenshots/api-keys.png)

API keys are managed from the Developer Portal. Full secrets are shown once on creation.

## Step 2: List channels

```bash
curl https://pinned.events/api/public/v1/channels \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json
{
  "docs": [
    {
      "id": 42,
      "name": "Valencia Live Music",
      "slug": "valencia-live-music"
    }
  ],
  "page": 1,
  "limit": 20,
  "totalDocs": 1,
  "totalPages": 1,
  "hasNextPage": false,
  "hasPrevPage": false
}
```

Choose the channelId where the event should appear. Channel access is checked again at request time, so a key cannot publish into channels the owner no longer owns or can access.

## Step 3: List public classifications

```bash
curl https://pinned.events/api/public/v1/classifications
```

```json
{
  "version": "2026-05-23",
  "docs": [
    {
      "slug": "music",
      "name": "Music",
      "description": "Concerts, DJ sets, live performances, and music events."
    }
  ]
}
```

Use public classification slugs such as music or workshop in the classifications field. The Public API does not require opaque internal taxonomy IDs.

## Step 4: Create a minimal event

```bash
curl https://pinned.events/api/public/v1/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wordpress-post-123-create" \
  -d '{
    "channelId": 42,
    "title": "Rooftop Jazz Night",
    "startAt": "2026-06-12T20:00:00-04:00",
    "timezone": "America/New_York",
    "classifications": ["music"],
    "approxLocation": {
      "city": {
        "placeId": "place-new-york",
        "name": "New York",
        "countryCode": "US"
      }
    }
  }'
```

```json
{
  "doc": {
    "id": 1,
    "title": "Rooftop Jazz Night"
  }
}
```

The minimal create request needs channelId, title, startAt, timezone, classifications, and an approximate city. Add venue details, descriptions, images, ticket offers, and publishing options as the integration grows.

## Step 5: Retry safely with Idempotency-Key

Use a stable Idempotency-Key for the same source event and action. If a webhook or import job retries the same create request, reuse the same key instead of generating a new random value.

```text
wordpress-post-123-create
hubspot-event-456-publish
airtable-recABC123-create
nightly-import-2026-06-12-row-42
```

## Step 6: Check the created event

After the request succeeds, open the related event or channel in Pinned Events and confirm the title, date, timezone, location, ticket link, publishing status, and channel placement. If your workflow creates drafts, review the event before publishing it.

![Published Pinned Events event page with RSVP, reminders, social planning, and ticket link actions.](https://pinned.events/developers/screenshots/event-rsvp.png)

API-created events appear in Pinned Events channels where people can discover, save, RSVP, and plan around them.

## Next steps

- Read Authentication before storing API keys in production.
- Read Create Events for optional fields, images, ticket links, and validation errors.
- Read Idempotency before wiring webhooks, scheduled imports, or retry queues.
- Use the interactive API reference when you need exact request and response schemas.

## Related pages

- [Authentication](https://pinned.events/developers/docs/authentication): Authenticate Public API requests with bearer API keys from trusted server-side code.
- [API keys and scopes](https://pinned.events/developers/docs/api-keys-and-scopes): Understand API key lifecycle, default quickstart scopes, owner-scoped channel access, and revocation behavior.
- [Channels](https://pinned.events/developers/docs/channels): Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Authentication

Authenticate Public API requests with bearer API keys from trusted server-side code.

Canonical URL: https://pinned.events/developers/docs/authentication

## Bearer API key authentication

The Pinned Events API authenticates Public API calls with API keys. The recommended public contract is Authorization: Bearer YOUR_API_KEY. Server-side integrations may also use x-api-key: YOUR_API_KEY where supported. Send exactly one authentication header.

```bash
curl https://pinned.events/api/public/v1/channels \
  -H "Authorization: Bearer YOUR_API_KEY"
```

> **Use one auth header:** Do not send both Authorization and x-api-key on the same request. Ambiguous authentication is rejected.

## Where to create API keys

Create API keys from the Developer Portal API key management page. Key management requires a signed-in user who has completed account setup.

## One-time secret behavior

The full API key secret is shown only once when the key is created. List and read endpoints never return the full secret, and the stored key material is hashed.

## Server-side storage

- Store API keys in environment variables or a secrets manager.
- Do not expose API keys in frontend JavaScript, public mobile clients, screenshots, logs, or repositories.
- Use one key per integration so revocation does not affect unrelated systems.
- Rotate keys if a key may have been exposed.

## Key rotation and revocation

To rotate a key, create a replacement key, deploy the new secret, verify traffic, then revoke the old key. Revoked keys cannot authenticate Public API requests and remain visible only as revocation metadata.

## 401 Unauthorized examples

A 401 response means the API key is missing, malformed, revoked, disabled, expired, or otherwise invalid.

```json
{
  "error": {
    "code": "invalid_api_key",
    "message": "API key is invalid, revoked, disabled, or expired."
  }
}
```

## Related pages

- [API keys and scopes](https://pinned.events/developers/docs/api-keys-and-scopes): Understand API key lifecycle, default quickstart scopes, owner-scoped channel access, and revocation behavior.
- [Security](https://pinned.events/developers/docs/security-best-practices): Store keys server-side, use least privilege, rotate and revoke keys, and keep secrets out of logs.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.
- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# API Keys and Scopes

Understand API key lifecycle, default quickstart scopes, owner-scoped channel access, and revocation behavior.

Canonical URL: https://pinned.events/developers/docs/api-keys-and-scopes

## What API keys are

An API key is a bearer secret tied to the user who created it. The key owner determines which channels can be listed or targeted by Public API requests.

## Default scopes

New keys start with the minimum quickstart pair: channels:read and events:create. Existing keys are not silently changed when defaults change.

| Scope | Allows |
| --- | --- |
| channels:read | List channels available to the key owner. |
| events:create | Create events in channels available to the key owner. |
| events:read | Read events available through the Public API. |
| events:update | Update supported event fields through the Public API. |
| media:create | Upload media for use as event cover images. |

## Owner access

Scopes are necessary but not enough. Runtime authorization still verifies that the key owner has access to the target channel before a create, read, or update operation succeeds.

## Missing scope behavior

When a valid API key does not include the scope required by a route, the API returns a 403 insufficient_scope response. Add only the missing scope that the integration actually needs.

## Revoked, disabled, and expired keys

Revoked, disabled, and expired keys cannot authenticate Public API requests. Soft-revoked keys remain in audit history but do not count toward the active key cap.

## Related pages

- [Authentication](https://pinned.events/developers/docs/authentication): Authenticate Public API requests with bearer API keys from trusted server-side code.
- [Channels](https://pinned.events/developers/docs/channels): Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.
- [Security](https://pinned.events/developers/docs/security-best-practices): Store keys server-side, use least privilege, rotate and revoke keys, and keep secrets out of logs.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Channels and channelId

Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.

Canonical URL: https://pinned.events/developers/docs/channels

## What a channel is

A channel is a public feed where organizers, venues, communities, or local platforms publish events. People can discover events through channels and follow updates from sources they care about.

![Pinned Events channels page showing public channels.](https://pinned.events/developers/screenshots/channels.png)

Channels are the publishing destination for Public API integrations.

## List accessible channels

```bash
curl https://pinned.events/api/public/v1/channels \
  -H "Authorization: Bearer YOUR_API_KEY"
```

```json
{
  "docs": [
    {
      "id": 42,
      "name": "Valencia Live Music",
      "slug": "valencia-live-music"
    }
  ],
  "page": 1,
  "limit": 20,
  "totalDocs": 1,
  "totalPages": 1,
  "hasNextPage": false,
  "hasPrevPage": false
}
```

## Why every event needs a channelId

Pinned Events publishes every API-created event into a channel. The channelId tells the API where the event should appear and gives the runtime a concrete resource for ownership and access checks.

## Forbidden channel examples

A 403 response means the key is valid but cannot access the requested channel. This can happen when the wrong channelId is used or when channel ownership changed after the key was created.

## Related pages

- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.
- [API keys and scopes](https://pinned.events/developers/docs/api-keys-and-scopes): Understand API key lifecycle, default quickstart scopes, owner-scoped channel access, and revocation behavior.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Create Events

Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.

Canonical URL: https://pinned.events/developers/docs/create-events

## Minimal event request

```bash
curl https://pinned.events/api/public/v1/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wordpress-post-123-create" \
  -d '{
    "channelId": 42,
    "title": "Rooftop Jazz Night",
    "startAt": "2026-06-12T20:00:00-04:00",
    "timezone": "America/New_York",
    "classifications": ["music"],
    "approxLocation": {
      "city": {
        "placeId": "place-new-york",
        "name": "New York",
        "countryCode": "US"
      }
    }
  }'
```

```json
{
  "doc": {
    "id": 1,
    "title": "Rooftop Jazz Night"
  }
}
```

## Required fields

| Field | Purpose |
| --- | --- |
| channelId | Numeric ID of the target channel. |
| title | Human-readable event title. |
| startAt | ISO datetime with offset. |
| timezone | IANA timezone identifier. |
| classifications | Public classification slugs such as music. |
| approxLocation.city.placeId | Place identifier for the event city. |

## Optional fields

As the workflow grows, add exact venue details, descriptions, images, ticket offers, endAt, attendance, audience, capacity, and publishing options.

![Create event endpoint in the interactive API reference.](https://pinned.events/developers/screenshots/api-reference-create-event.png)

The reference shows every supported create-event request field and response schema.

## Expanded event request

Use the minimal request for the first integration test. Add fields such as endAt, status, locationName, coverImage, and ticket offers when your publishing workflow is ready to send richer event data.

```json
{
  "channelId": 42,
  "title": "Rooftop Jazz Night",
  "startAt": "2026-06-12T20:00:00-04:00",
  "endAt": "2026-06-12T22:00:00-04:00",
  "timezone": "America/New_York",
  "status": "published",
  "classifications": ["music"],
  "approxLocation": {
    "city": {
      "placeId": "place-new-york",
      "name": "New York",
      "countryCode": "US"
    }
  },
  "locationName": "Rooftop Room",
  "coverImage": 123,
  "offers": [
    {
      "name": "General admission",
      "url": "https://tickets.example/rooftop-jazz-night",
      "availability": "available"
    }
  ]
}
```

## Location and classifications

Use public classification slugs in classifications. List supported values with GET /api/public/v1/classifications. Do not send internal taxonomy IDs in the public create-event payload.

## Images, media, and ticket links

Upload image files with POST /api/public/v1/media, then use the returned media doc id as coverImage. External ticketing can stay outside Pinned Events; include ticket offer URLs when your workflow supports them.

## Draft vs published events

Use status draft when a human should review imported data before it appears publicly. Use status published only when the source record has already been approved for public distribution.

## Validation errors

```json
{
  "error": {
    "code": "invalid_request",
    "message": "Request validation failed.",
    "requestId": "req_01J...",
    "details": {
      "field": "classifications",
      "issue": "Unknown classification slug \"gig\". Use GET /api/public/v1/classifications to list supported values."
    }
  }
}
```

## Related pages

- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.
- [Channels](https://pinned.events/developers/docs/channels): Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.
- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.
- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.
- [Examples](https://pinned.events/developers/docs/examples): Copy production-oriented integration patterns for CMS, CRM, forms, spreadsheets, nightly imports, Node.js, and Python.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Idempotency and Safe Retries

Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.

Canonical URL: https://pinned.events/developers/docs/idempotency

## What idempotency means

Idempotency lets a client retry the same create-event operation safely. The same Idempotency-Key with the same canonical payload returns the cached successful response instead of creating another event.

## When to use Idempotency-Key

Use Idempotency-Key on create-event requests from webhooks, scheduled imports, queues, or any automation that may retry after a timeout or network failure.

## Recommended key format

```text
cms-post-123-create
crm-event-456-publish
partner-form-789-create
nightly-import-2026-06-12-row-42
```

## Same key and same payload

When the same Idempotency-Key is retried with the same canonical payload after a successful create request, the API replays the cached success response instead of creating another event.

## Same key and different payload

If the same Idempotency-Key is reused with a different payload, the API returns a conflict. Generate keys from stable source IDs and actions, not from timestamps created during retry.

## Duplicate prevention

Duplicate prevention depends on sending the same stable key for the same source event and action. Do not generate a new random idempotency key for each retry of the same source event.

## Retention duration

Succeeded idempotency entries are retained for the configured Public API idempotency window. The default succeeded-entry TTL is 15 minutes, and in-progress entries use a shorter default TTL of 45 seconds.

## Related pages

- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.
- [Rate limits](https://pinned.events/developers/docs/rate-limits): Understand 429 responses, Retry-After handling, and retry strategies for busy integrations.
- [Examples](https://pinned.events/developers/docs/examples): Copy production-oriented integration patterns for CMS, CRM, forms, spreadsheets, nightly imports, Node.js, and Python.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# API Errors and Status Codes

Understand the error envelope, common status codes, and how to debug failed Public API requests.

Canonical URL: https://pinned.events/developers/docs/errors

## Error response format

```json
{
  "error": {
    "code": "invalid_request",
    "message": "Request validation failed.",
    "requestId": "req_01J...",
    "details": {
      "field": "classifications",
      "issue": "Unknown classification slug \"gig\". Use GET /api/public/v1/classifications to list supported values."
    }
  }
}
```

## Status codes

| Status | Meaning |
| --- | --- |
| 400 invalid_request | The request body, query, or headers are invalid. |
| 401 invalid_api_key | The API key is missing, malformed, revoked, disabled, or expired. |
| 403 forbidden or insufficient_scope | The key is valid but lacks scope or channel access. |
| 404 not_found | The requested resource does not exist or is not accessible. |
| 409 conflict | The request conflicts with existing state, usually idempotency. |
| 429 rate_limited | The request exceeded a route or policy limit. |
| 500 internal_error | An unexpected server error occurred. |

## How to debug

- Check the requestId in logs when contacting support.
- Confirm the API key is active and has the required scope.
- Confirm the key owner still has access to the target channelId.
- Validate classifications with GET /api/public/v1/classifications.
- Use stable Idempotency-Key values for retries.

## Related pages

- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.
- [Rate limits](https://pinned.events/developers/docs/rate-limits): Understand 429 responses, Retry-After handling, and retry strategies for busy integrations.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Rate Limits

Understand 429 responses, Retry-After handling, and retry strategies for busy integrations.

Canonical URL: https://pinned.events/developers/docs/rate-limits

## What is rate limited

Public API routes have runtime rate limits to keep integrations predictable and protect the platform. API key creation is also limited for abuse prevention.

## 429 response

When a request is rate limited, the API returns 429 rate_limited. Honor Retry-After when present and use exponential backoff with jitter.

## Rate-limit headers

Rate-limited responses include X-RateLimit-* headers and Retry-After when retry timing is available. Use Retry-After as the earliest retry time and add exponential backoff with jitter for repeated failures.

## Backoff strategy

Use exponential backoff with jitter for repeated 429 or transient 5xx responses. Retry create-event requests with the same Idempotency-Key so a delayed retry cannot create duplicate event listings.

## How idempotency helps with retries

For create-event requests, keep the same Idempotency-Key while retrying the same source event. This prevents duplicate event listings after a timeout or transient failure.

## Related pages

- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.
- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.
- [Security](https://pinned.events/developers/docs/security-best-practices): Store keys server-side, use least privilege, rotate and revoke keys, and keep secrets out of logs.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Pagination

Understand pagination fields returned by list endpoints, including page, limit, totalDocs, totalPages, hasNextPage, and hasPrevPage.

Canonical URL: https://pinned.events/developers/docs/pagination

## How pagination works

Public list endpoints use page-based pagination. Send page and limit query parameters, then follow hasNextPage until the collection is exhausted.

```bash
curl "https://pinned.events/api/public/v1/events?page=1&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

## Response shape

```json
{
  "docs": [
    {
      "id": 42,
      "name": "Valencia Live Music",
      "slug": "valencia-live-music"
    }
  ],
  "page": 1,
  "limit": 20,
  "totalDocs": 1,
  "totalPages": 1,
  "hasNextPage": false,
  "hasPrevPage": false
}
```

| Field | Meaning |
| --- | --- |
| page | Current page number. |
| limit | Items requested per page; list endpoints cap the effective limit at 100. |
| totalDocs | Total number of matching records when the endpoint returns a full pagination envelope. |
| totalPages | Total number of pages when the endpoint returns a full pagination envelope. |
| hasNextPage | Whether another page exists in full pagination envelopes. |
| hasPrevPage | Whether a previous page exists in full pagination envelopes. |

## Endpoint differences

GET /api/public/v1/channels returns the full pagination envelope. GET /api/public/v1/events returns a minimal envelope with docs, totalDocs, limit, and page; derive additional pages from page, limit, and totalDocs.

## Empty results

An empty page returns docs as an empty array. Treat empty docs as a valid response, not as an error.

## Related pages

- [Channels](https://pinned.events/developers/docs/channels): Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.
- [Versioning and OpenAPI](https://pinned.events/developers/docs/versioning-and-openapi): Use the OpenAPI schema, versioned /api/public/v1 routes, and documented deprecation policy for production integrations.
- [Examples](https://pinned.events/developers/docs/examples): Copy production-oriented integration patterns for CMS, CRM, forms, spreadsheets, nightly imports, Node.js, and Python.
- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Versioning and OpenAPI

Use the OpenAPI schema, versioned /api/public/v1 routes, and documented deprecation policy for production integrations.

Canonical URL: https://pinned.events/developers/docs/versioning-and-openapi

## Current version

The public endpoint namespace is /api/public/v1. The current generated schema is published as public-api-v1 and can be downloaded from the OpenAPI YAML URL.

## OpenAPI YAML

```bash
curl https://pinned.events/api/developer/openapi/public-api-v1
```

Use the OpenAPI file to generate clients, import into Postman or Insomnia, or validate request and response contracts in CI.

## Breaking changes and deprecations

Breaking changes should use a new versioned namespace. Deprecated fields or behaviors are documented before removal when possible.

## Client generation

Use the OpenAPI YAML with client generators or API tooling such as Postman and Insomnia. Generated clients should still keep API keys server-side and should not be bundled into frontend code with secrets.

## Related pages

- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Pagination](https://pinned.events/developers/docs/pagination): Understand pagination fields returned by list endpoints, including page, limit, totalDocs, totalPages, hasNextPage, and hasPrevPage.
- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# API Security Best Practices

Store keys server-side, use least privilege, rotate and revoke keys, and keep secrets out of logs.

Canonical URL: https://pinned.events/developers/docs/security-best-practices

## Store keys server-side

- Keep API keys in server-side environment variables or a secrets manager.
- Never place API keys in browser JavaScript, public mobile apps, static config files, or repositories.
- Do not log full API keys, key hashes, sensitive request payloads, or private event content.

## Use least privilege

Give each integration only the scopes it needs. Use one key per integration so revoking a compromised key has a small blast radius.

## Rotate and revoke keys

Create a replacement key, deploy the new secret, verify traffic, then revoke the old key. Revocation is soft and preserves audit metadata.

## Safe logging

Log request IDs, status codes, route names, and non-sensitive integration identifiers. Do not log full API keys, key hashes, Authorization headers, private event payloads, or partner-submitted secrets.

## Related pages

- [Authentication](https://pinned.events/developers/docs/authentication): Authenticate Public API requests with bearer API keys from trusted server-side code.
- [API keys and scopes](https://pinned.events/developers/docs/api-keys-and-scopes): Understand API key lifecycle, default quickstart scopes, owner-scoped channel access, and revocation behavior.
- [Rate limits](https://pinned.events/developers/docs/rate-limits): Understand 429 responses, Retry-After handling, and retry strategies for busy integrations.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Troubleshooting

Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.

Canonical URL: https://pinned.events/developers/docs/troubleshooting

## 401 Unauthorized

- Confirm the Authorization header uses Bearer YOUR_API_KEY.
- Confirm the full secret was copied when the key was created.
- Confirm the key has not been revoked, disabled, or expired.

## 403 Forbidden

- Confirm the API key has the required scope.
- Confirm the key owner can still access the target channelId.
- Confirm the channelId belongs to the account that created the key.

## Missing scope

A missing scope produces a 403 response after the API key is authenticated. Check the route documentation for the required scope and add only that permission to the integration key.

## Wrong channelId

If the channelId is valid but not accessible to the key owner, the API rejects the request. Call GET /api/public/v1/channels with the same key and use one of the returned channel IDs.

## Validation errors

Validation errors usually mean a required field is missing, a date or timezone is invalid, a public classification slug is unknown, or an ID refers to a resource the key cannot use.

## Duplicate events

- Send an Idempotency-Key header on create-event requests.
- Use the same key when retrying the same source event and action.
- Do not generate a new random idempotency key on every retry.

## Rate limits

A 429 response means the request exceeded a route, IP, key, or key-management policy limit. Honor Retry-After when present and retry with backoff.

## Lost API key secret

Full API key secrets are shown only once. If a secret is lost, create a replacement key, deploy it, verify the integration, then revoke the old key.

## Event not visible

- Check whether the event was created as a draft or published listing.
- Check the event date, timezone, and channel placement.
- Check ticket links, images, and required moderation or review states.

## Related pages

- [Errors](https://pinned.events/developers/docs/errors): Understand the error envelope, common status codes, and how to debug failed Public API requests.
- [Authentication](https://pinned.events/developers/docs/authentication): Authenticate Public API requests with bearer API keys from trusted server-side code.
- [Channels](https://pinned.events/developers/docs/channels): Every event is published into a channel. Use GET /api/public/v1/channels to find accessible channelId values.
- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.
- [Rate limits](https://pinned.events/developers/docs/rate-limits): Understand 429 responses, Retry-After handling, and retry strategies for busy integrations.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Integration Examples

Copy production-oriented integration patterns for CMS, CRM, forms, spreadsheets, nightly imports, Node.js, and Python.

Canonical URL: https://pinned.events/developers/docs/examples

## CMS approved content to create event

1. Content editor approves an event entry.
2. CMS webhook sends the entry to your backend.
3. Backend maps CMS fields to the Pinned Events payload.
4. Backend creates a draft or published event in the target channel.

## CRM or back-office record to create event

1. Operations marks a venue, host, artist, class, or schedule record as approved.
2. A backend job maps the internal record into the Public API create-event payload.
3. The job creates a draft or published event in the selected channel.
4. Future update jobs can keep source data and Pinned Events aligned.

## Partner forms and spreadsheets

1. A partner submits event details through a form, Airtable base, Google Sheet, or admin form.
2. Your team validates the submission in your own workflow.
3. Approved rows are sent to the API with stable source IDs and idempotency keys.
4. Events are created as drafts when editorial review is still needed.

## Nightly import and high-volume jobs

For nightly imports, use stable source IDs, batch logs, idempotency keys, and retry queues. Do not resend duplicate rows without a stable Idempotency-Key strategy.

## Retry-safe webhook handler

Webhook providers often retry after timeouts. Build the Idempotency-Key from the source provider event ID and action so every retry of the same source event uses the same key.

## Node.js fetch example

```ts
const response = await fetch('https://pinned.events/api/public/v1/events', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.PINNED_EVENTS_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'cms-post-123-create',
  },
  body: JSON.stringify({
    channelId: 42,
    title: 'Rooftop Jazz Night',
    startAt: '2026-06-12T20:00:00-04:00',
    timezone: 'America/New_York',
    classifications: ['music'],
    approxLocation: {
      city: {
        placeId: 'place-new-york',
        name: 'New York',
        countryCode: 'US',
      },
    },
  }),
});
```

## Python requests example

```python
import os
import requests

response = requests.post(
    'https://pinned.events/api/public/v1/events',
    headers={
        'Authorization': f"Bearer {os.environ['PINNED_EVENTS_API_KEY']}",
        'Content-Type': 'application/json',
        'Idempotency-Key': 'cms-post-123-create',
    },
    json={
        'channelId': 42,
        'title': 'Rooftop Jazz Night',
        'startAt': '2026-06-12T20:00:00-04:00',
        'timezone': 'America/New_York',
        'classifications': ['music'],
        'approxLocation': {
            'city': {
                'placeId': 'place-new-york',
                'name': 'New York',
                'countryCode': 'US',
            },
        },
    },
)
response.raise_for_status()
```

## Related pages

- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Idempotency](https://pinned.events/developers/docs/idempotency): Use stable Idempotency-Key headers so webhook retries and import jobs do not create duplicate event listings.
- [Security](https://pinned.events/developers/docs/security-best-practices): Store keys server-side, use least privilege, rotate and revoke keys, and keep secrets out of logs.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Developer API FAQ

Quick answers to common developer and product questions about the Pinned Events API.

Canonical URL: https://pinned.events/developers/docs/faq

## Common questions

| Question | Answer |
| --- | --- |
| What is the Pinned Events API? | A REST API for publishing events into Pinned Events channels for local discovery, RSVP, reminders, and social planning. |
| Can I create events from my CMS? | Yes. Use your backend or CMS webhook to create draft or published events through POST /api/public/v1/events. |
| Can I publish events from a CRM? | Yes. CRM or back-office records can map to Public API event payloads. |
| Do I need a channelId? | Yes. Every event is published into a channel owned or accessible by the API key owner. |
| Can I keep existing ticketing? | Yes. Use Pinned Events for discovery and planning while keeping external ticketing for checkout. |
| How do I avoid duplicate events? | Use a stable Idempotency-Key for retries of the same source event. |
| Can I use the API from frontend JavaScript? | No. API keys are bearer secrets and must stay on trusted server-side systems. |
| What scopes do I need? | New quickstart keys start with channels:read and events:create. Add read, update, or media scopes only when the integration needs them. |
| Does the API support OpenAPI? | Yes. The OpenAPI YAML is publicly available from /api/developer/openapi/public-api-v1. |
| Does the API support webhooks? | Inbound event publishing from your webhooks is supported through your backend. Outbound Pinned Events webhooks are not documented as part of this release. |

## Related pages

- [Getting started](https://pinned.events/developers/docs/getting-started): Create an API key, find a channelId, publish your first event, and retry safely.
- [Authentication](https://pinned.events/developers/docs/authentication): Authenticate Public API requests with bearer API keys from trusted server-side code.
- [Create events](https://pinned.events/developers/docs/create-events): Create draft or published events with required fields first, then add location, media, ticketing, and publishing options.
- [Troubleshooting](https://pinned.events/developers/docs/troubleshooting): Diagnose authentication failures, forbidden channel access, validation errors, duplicate events, and rate limits.

## Core resources

- [Developer docs](https://pinned.events/developers/docs)
- [API reference](https://pinned.events/developers/reference)
- [OpenAPI YAML](https://pinned.events/api/developer/openapi/public-api-v1)
- [Developer changelog](https://pinned.events/developers/changelog)


# Developer API changelog



Canonical URL: https://pinned.events/developers/changelog



## 2026-05-23 - Public API v1 self-service launch



- Public developer overview and documentation are available.

- API key management is available for authenticated, onboarded users.

- Public API reference and OpenAPI YAML are available.

- Default new key scopes include channels:read and events:create.

- Public classifications are available through GET /api/public/v1/classifications.
