Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/agendaforge-app:agendaforge-app-records-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: AgendaForge Public REST Records API
version: 1.0.0
summary: 'Server-to-server REST API for the AgendaForge event-management platform: webhooks (REST Hooks), sample records, cursor-paginated list endpoints, create actions and batch create for contacts, sessions, and sponsors, and async contact and ticket imports with pollable jobs.'
description: 'The HTTP API that powers the AgendaForge Zapier, Make.com, and n8n integrations, and any custom server-to-server integration.
Every request is authenticated with an organization API key (created in Settings > API Tokens; keys begin with afk_live_ and are shown once). Keys carry named scopes chosen at creation: public_api (this API, on by default), mcp:read and mcp:write (the AgendaForge MCP server for AI clients), and tickets:import (ticket imports, never on by default). Every endpoint here requires the public_api scope, except POST /api/v1/imports/tickets, which requires tickets:import; requests with a key that lacks the required scope receive 403 FORBIDDEN.
All requests and responses are JSON. Every error, 4xx and 5xx alike, is JSON of the shape { "error": { "code": "...", "message": "..." } } with a machine-readable code and a human-readable resolution hint. Unknown paths under /api/v1 return 404 NOT_FOUND with the list of valid endpoints.
Pagination: list endpoints (GET /contacts, GET /sessions, GET /sponsors) are cursor-based. Request a page with ?limit= (1-100, default 25), then follow nextCursor via ?cursor= until hasMore is false. Cursors are opaque continuation tokens; never construct one.
Long-running work: POST /api/v1/imports/contacts answers 202 Accepted with a job id and a statusUrl (also in the Location header); poll GET /api/v1/jobs/{id} until status reaches completed or failed. For up to 100 items in one synchronous call, use POST /api/v1/contacts/batch instead. POST /api/v1/imports/tickets finishes up to 100 tickets inside the request (200, job already completed) and queues 101 to 1,000 tickets (202, poll the same way).
Rate limits: 120 requests per minute per API key (fixed window), and 30 requests per minute per IP for unauthenticated requests. Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset (seconds until the window resets), and RateLimit-Policy headers so clients can self-throttle; exceeding the window returns 429 RATE_LIMITED with a Retry-After header.
Versioning and deprecation: the API is versioned in the URL path (/api/v1). Additive changes (new endpoints, new optional fields, new webhook events) happen within a version and are never breaking; breaking changes ship only as a new version path. If an endpoint or version is ever deprecated it will be announced in the changelog at least 180 days before removal and signaled with Deprecation and Sunset headers on affected responses. Nothing is deprecated today.
Human-readable reference: https://agendaforge.app/docs/api. Developer portal: https://agendaforge.app/developers. Site content for agents: https://agendaforge.app/llms.txt.'
contact:
name: AgendaForge support
email: hello@agendaforge.app
url: https://agendaforge.app/docs/api
termsOfService: https://agendaforge.app/terms
servers:
- url: https://api.agendaforge.app
description: Production
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
tags:
- name: Records
description: Cursor-paginated lists, create actions, and batch create for contacts, sessions, and sponsors.
paths:
/api/v1/contacts:
get:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: listContacts
tags:
- Records
summary: List contacts (cursor-paginated)
description: Lists the organization's contacts, newest first. Page with ?limit= (1-100, default 25) and ?cursor= (the previous response's nextCursor). The cursor is opaque; never construct one. Objects match the Contact shape emitted by webhooks and POST /api/v1/contacts.
parameters:
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Cursor'
responses:
'200':
description: One page of contacts, newest first.
content:
application/json:
schema:
type: object
required:
- data
- hasMore
- nextCursor
properties:
data:
type: array
items:
$ref: '#/components/schemas/Contact'
hasMore:
type: boolean
description: True when another page exists after this one.
nextCursor:
type:
- string
- 'null'
description: Opaque cursor for the next page; pass it back as ?cursor=. Null on the last page.
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
post:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: createContact
tags:
- Records
summary: Create a contact
description: Creates a contact in the key's organization and fires the contact.added trigger. Duplicate emails are rejected with 400 VALIDATION_ERROR.
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateContactRequest'
responses:
'200':
description: The created contact.
content:
application/json:
schema:
$ref: '#/components/schemas/Contact'
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
/api/v1/contacts/batch:
post:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: createContactsBatch
tags:
- Records
summary: Create up to 100 contacts in one request
description: 'Synchronous batch create. Each item behaves exactly like a single POST /api/v1/contacts request (same validation, same contact.added trigger). Items succeed or fail independently: the response pairs every input index with either the created contact or a typed error, so one duplicate email never fails the whole batch. For larger sets use POST /api/v1/imports/contacts.'
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BatchContactsRequest'
responses:
'200':
description: Per-item results in input order plus a summary. HTTP 200 even when some items fail; inspect results[].status.
content:
application/json:
schema:
$ref: '#/components/schemas/BatchContactsResponse'
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
/api/v1/sessions:
get:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: listSessions
tags:
- Records
summary: List an event's sessions (cursor-paginated)
description: Lists sessions on one event owned by the key's organization, newest first. eventId is required; page with ?limit= (1-100, default 25) and ?cursor=. Objects match the Session shape emitted by webhooks and POST /api/v1/sessions.
parameters:
- $ref: '#/components/parameters/EventId'
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Cursor'
responses:
'200':
description: One page of sessions, newest first.
content:
application/json:
schema:
type: object
required:
- data
- hasMore
- nextCursor
properties:
data:
type: array
items:
$ref: '#/components/schemas/Session'
hasMore:
type: boolean
description: True when another page exists after this one.
nextCursor:
type:
- string
- 'null'
description: Opaque cursor for the next page; pass it back as ?cursor=. Null on the last page.
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
post:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: createSession
tags:
- Records
summary: Create a session
description: Creates a session on an event owned by the key's organization and fires the session.added trigger. A session must be accepted before it can be scheduled. Supply an accepted-category statusId from this event when assigning a time.
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSessionRequest'
responses:
'200':
description: The created session.
content:
application/json:
schema:
$ref: '#/components/schemas/Session'
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
/api/v1/sponsors:
get:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: listSponsors
tags:
- Records
summary: List an event's sponsors (cursor-paginated)
description: Lists sponsors on one event owned by the key's organization, newest first. eventId is required; page with ?limit= (1-100, default 25) and ?cursor=. Objects match the Sponsor shape emitted by webhooks and POST /api/v1/sponsors.
parameters:
- $ref: '#/components/parameters/EventId'
- $ref: '#/components/parameters/Limit'
- $ref: '#/components/parameters/Cursor'
responses:
'200':
description: One page of sponsors, newest first.
content:
application/json:
schema:
type: object
required:
- data
- hasMore
- nextCursor
properties:
data:
type: array
items:
$ref: '#/components/schemas/Sponsor'
hasMore:
type: boolean
description: True when another page exists after this one.
nextCursor:
type:
- string
- 'null'
description: Opaque cursor for the next page; pass it back as ?cursor=. Null on the last page.
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
post:
security:
- bearerApiKey:
- public_api
- headerApiKey:
- public_api
x-required-scopes:
- public_api
operationId: createSponsor
tags:
- Records
summary: Create a sponsor
description: Creates a sponsor on an event owned by the key's organization and fires the sponsor.added trigger.
parameters:
- $ref: '#/components/parameters/IdempotencyKey'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateSponsorRequest'
responses:
'200':
description: The created sponsor.
content:
application/json:
schema:
$ref: '#/components/schemas/Sponsor'
'400':
$ref: '#/components/responses/ValidationError'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/IdempotencyConflict'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/InternalError'
default:
$ref: '#/components/responses/UnexpectedError'
components:
parameters:
Limit:
name: limit
in: query
required: false
description: Page size, 1-100. Defaults to 25; values above 100 are clamped to 100.
schema:
type: integer
minimum: 1
maximum: 100
default: 25
IdempotencyKey:
name: Idempotency-Key
in: header
required: false
description: 'Optional key for safe retries, retained for 24 hours. Repeating the same request on the same endpoint returns the original response and sets Idempotent-Replay: true. Reusing the key on a different endpoint returns 409.'
schema:
type: string
minLength: 1
maxLength: 255
EventId:
name: eventId
in: query
required: true
description: The event to list within; must be owned by the key's organization (anything else answers 403 FORBIDDEN).
schema:
type: string
Cursor:
name: cursor
in: query
required: false
description: Opaque continuation token from the previous response's nextCursor. Omit for the first page. A malformed cursor answers 400 VALIDATION_ERROR.
schema:
type: string
schemas:
CreateContactRequest:
type: object
required:
- firstName
- lastName
- email
properties:
type:
type: string
description: speaker | sponsor | attendee | vendor | partner | exhibitor | other. Defaults to "attendee".
firstName:
type: string
lastName:
type: string
email:
type: string
format: email
phone:
type: string
company:
type: string
jobTitle:
type: string
Error:
type: object
required:
- error
description: Every non-2xx response carries this JSON shape. The code is machine-readable; the message is a human-readable resolution hint.
properties:
error:
type: object
required:
- code
- message
properties:
code:
type: string
enum:
- UNAUTHENTICATED
- FORBIDDEN
- VALIDATION_ERROR
- PAYMENT_REQUIRED
- NOT_FOUND
- RATE_LIMITED
- CONFLICT
- INTERNAL
message:
type: string
BatchContactsResponse:
type: object
required:
- results
- summary
properties:
results:
type: array
description: One entry per input item, in input order.
items:
type: object
required:
- index
- status
properties:
index:
type: integer
description: Position in the request array.
status:
type: string
enum:
- created
- error
contact:
$ref: '#/components/schemas/Contact'
error:
type: object
description: Present when status is "error"; same code vocabulary as top-level errors.
required:
- code
- message
properties:
code:
type: string
message:
type: string
summary:
type: object
required:
- total
- created
- failed
properties:
total:
type: integer
created:
type: integer
failed:
type: integer
CreateSessionRequest:
type: object
required:
- eventId
- title
properties:
eventId:
type: string
description: The event the session belongs to; must be owned by the key's organization.
title:
type: string
statusId:
type: string
description: A session status id from this event. A session must be accepted before it can be scheduled.
type:
type: string
description: keynote | talk | panel | workshop | lightning | break | networking | social | placeholder | other. Defaults to "talk".
description:
type: string
duration:
type: number
description: Minutes; falls back to the type's default duration when omitted.
startTime:
type: string
description: ISO-8601 UTC timestamp.
endTime:
type: string
description: ISO-8601 UTC timestamp.
maxAttendees:
type: number
isPublic:
type: boolean
tags:
type: array
items:
type: string
BatchContactsRequest:
type: object
required:
- contacts
properties:
contacts:
type: array
minItems: 1
maxItems: 100
items:
$ref: '#/components/schemas/CreateContactRequest'
Contact:
type: object
description: Emitted identically by webhook deliveries, trigger samples, and the create response.
properties:
id:
type: string
type:
type: string
description: speaker | sponsor | attendee | vendor | partner | exhibitor | other.
firstName:
type: string
lastName:
type: string
email:
type: string
phone:
type:
- string
- 'null'
company:
type:
- string
- 'null'
jobTitle:
type:
- string
- 'null'
bio:
type:
- string
- 'null'
avatarUrl:
type:
- string
- 'null'
location:
type:
- string
- 'null'
website:
type:
- string
- 'null'
socialLinks:
type:
- object
- 'null'
description: Map of platform to URL.
expertiseAreas:
type: array
items:
type: string
tags:
type: array
items:
type: string
customFields:
type:
- object
- 'null'
description: Organization-defined custom fields.
source:
type:
- string
- 'null'
status:
type: string
rating:
type:
- number
- 'null'
createdAt:
type: string
description: ISO-8601 UTC timestamp.
events:
type: array
items:
$ref: '#/components/schemas/ContactEventRef'
ContactEventRef:
type: object
description: An event a contact is a member of.
properties:
eventId:
type: string
eventName:
type: string
eventNumber:
type:
- number
- 'null'
role:
type: string
status:
type: string
CreateSponsorRequest:
type: object
required:
- eventId
- name
properties:
eventId:
type: string
description: The event the sponsor belongs to; must be owned by the key's organization.
name:
type: string
website:
type: string
description:
type: string
hasBooth:
type: boolean
tierId:
type: string
description: A sponsor tier id scoped to the same event; validated server-side.
Sponsor:
type: object
description: Emitted identically by webhook deliveries, trigger samples, and the create response.
properties:
id:
type: string
eventId:
type: string
orgId:
type: string
name:
type: string
tierId:
type:
- string
- 'null'
tierName:
type:
- string
- 'null'
website:
type:
- string
- 'null'
description:
type:
- string
- 'null'
logoUrl:
type:
- string
- 'null'
logoLightUrl:
type:
- string
- 'null'
hasBooth:
type: boolean
assignedPortal:
type:
- string
- 'null'
primaryContactId:
type:
- string
- 'null'
primaryContact:
oneOf:
- $ref: '#/components/schemas/ContactSummary'
- type: 'null'
customFields:
type:
- object
- 'null'
submissionId:
type:
- string
- 'null'
createdAt:
type: string
description: ISO-8601 UTC timestamp.
Session:
type: object
description: Emitted identically by webhook deliveries, trigger samples, and the create response.
properties:
id:
type: string
eventId:
type: string
eventName:
type: string
eventNumber:
type:
- number
- 'null'
orgId:
type: string
title:
type: string
description:
type:
- string
- 'null'
type:
type: string
status:
type: string
trackId:
type:
- string
- 'null'
trackName:
type:
- string
- 'null'
roomId:
type:
- string
- 'null'
roomName:
type:
- string
- 'null'
startTime:
type:
- string
- 'null'
endTime:
type:
- string
- 'null'
duration:
type: number
description: Minutes.
maxAttendees:
type:
- number
- 'null'
tags:
type: array
items:
type: string
isPublic:
type: boolean
isTentative:
type: boolean
parentSessionId:
type:
- string
- 'null'
customFields:
type:
- object
- 'null'
source:
type:
- string
- 'null'
createdAt:
type: string
description: ISO-8601 UTC timestamp.
speakers:
type: array
items:
$ref: '#/components/schemas/SessionSpeakerRef'
ContactSummary:
type: object
properties:
id:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
phone:
type:
- string
- 'null'
company:
type:
- string
- 'null'
jobTitle:
type:
- string
- 'null'
SessionSpeakerRef:
type: object
properties:
contactId:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
role:
type: string
isConfirmed:
type: boolean
responses:
ValidationError:
description: A required field is missing or invalid, an unknown event was supplied, or a referenced record is not owned by the key's organization.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: VALIDATION_ERROR
message: firstName, lastName and email are required.
IdempotencyConflict:
description: The Idempotency-Key was already used on a different endpoint within the 24 hour retention window.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: CONFLICT
message: This Idempotency-Key was already used on a different endpoint. Use a new key.
Unauthenticated:
description: The API key is missing, malformed, invalid, expired, or revoked.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: UNAUTHENTICATED
message: 'Missing API key. Send ''Authorization: Bearer <key>''.'
UnexpectedError:
description: Any other failure, including 404 NOT_FOUND for an unknown path and transient 5xx errors. Always the same JSON error contract.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: INTERNAL
message: Unexpected server error. Retry with exponential backoff; if it persists, contact hello@agendaforge.app.
InternalError:
description: An unexpected server failure. The body still follows the JSON error contract; retry with exponential backoff.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: INTERNAL
message: Unexpected server error. Retry with exponential backoff; if it persists, contact hello@agendaforge.app.
RateLimited:
description: The fixed window for this API key (120 requests per minute) or IP (30 per minute, unauthenticated) is exhausted. Wait Retry-After seconds and retry.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: RATE_LIMITED
message: Rate limit exceeded. Retry after the number of seconds in the Retry-After header.
headers:
Retry-After:
description: Seconds to wait before retrying (RFC 6585).
schema:
type: string
RateLimit-Limit:
description: Requests allowed per window. Sent on every /api/v1 response, not only 429s.
schema:
type: string
RateLimit-Remaining:
description: Requests left in the current window.
schema:
type: string
RateLimit-Reset:
description: Seconds until the current window resets.
schema:
type: string
RateLimit-Policy:
description: The policy as "<limit>;w=<window seconds>", e.g. "120;w=60".
schema:
type: string
NotFound:
description: No such endpoint under /api/v1. The message lists the valid endpoints.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: NOT_FOUND
message: No such endpoint. See https://agendaforge.app/openapi.json for the API surface.
Forbidden:
description: The API key is valid but does not include the public_api scope. Create a key with the public_api scope in Settings > API Tokens.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: FORBIDDEN
message: This API key does not include the "public_api" scope. Create a key with the public_api scope in Settings > API Tokens.
securitySchemes:
bearerApiKey:
type: http
scheme: bearer
bearerFormat: afk_live_ organization API key
description: 'Organization API key sent as "Authorization: Bearer afk_live_...". Keys are created in Settings > API Tokens with named scopes (see x-api-key-scopes): public_api grants this REST API; mcp:read and mcp:write grant the AgendaForge MCP server; tickets:import grants ticket imports. Every endpoint here requires the public_api scope except POST /api/v1/imports/tickets, which requires tickets:import.'
x-scopes:
- public_api
- mcp:read
- mcp:write
- tickets:import
headerApiKey:
type: apiKey
in: header
name: X-API-Key
description: The same afk_live_ organization API key sent as an X-API-Key header instead of a Bearer token. Identical scope semantics to bearerApiKey.
x-scopes:
- public_api
- mcp:read
- mcp:write
- tickets:import
externalDocs:
description: AgendaForge API reference (human-readable)
url: https://agendaforge.app/docs/api
x-api-key-scopes:
- name: public_api
description: Grants the public REST API v1 (this specification). On by default when a key is created. Required by every endpoint here except POST /api/v1/imports/tickets; a key without it receives 403 FORBIDDEN.
# --- truncated at 32 KB (32 KB total) ---
# Full source: https://artifacts.apis.io/agendaforge-app/openapi/agendaforge-app-records-api-openapi.yml