agendaforge-app Records API

Cursor-paginated lists, create actions, and batch create for contacts, sessions, and sponsors.

Operations 7

GET /api/v1/contacts List contacts (cursor-paginated) #
POST /api/v1/contacts Create a contact #
POST /api/v1/contacts/batch Create up to 100 contacts in one request #
GET /api/v1/sessions List an event's sessions (cursor-paginated) #
POST /api/v1/sessions Create a session #
GET /api/v1/sponsors List an event's sponsors (cursor-paginated) #
POST /api/v1/sponsors Create a sponsor #

Work with this as data

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 Specification

agendaforge-app-records-api-openapi.yml Raw ↑
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