> ## Documentation Index
> Fetch the complete documentation index at: https://agenticadvertisingorg-feature-feedback.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Create or adopt my organization

> Bootstrap the caller's organization explicitly. Use this when the caller wants to control the organization name, `company_type`, `revenue_tier`, or `is_personal` flag before any agents are registered.

**Most storefront-style integrations don't need this call** — `POST /api/me/agents` will auto-create an org for a fresh OAuth user (corporate or personal workspace based on the email domain) and surface `org_auto_created: true` in the response. Reach for `POST /api/organizations` only when the auto-derived defaults aren't acceptable.

Three outcomes depending on the caller's state:

- **Fresh create** (most common): a new WorkOS organization is created, the caller is added as `owner`, the corporate domain is recorded as email-verified, and ToS / privacy-policy acceptance is logged from the request context. Returns `{ success: true, organization: { id, name } }`.

- **Prospect adoption**: an organization with the caller's email domain already exists as a `prospect` (the registry pre-recorded it from a brand crawl but no human had claimed it yet). The caller is promoted to `owner` of the existing record instead of forking a duplicate. Returns `{ id, name, adopted: true }`.

- **Already-active conflict**: the org exists and is already claimed by another paying member or a previously joined user. Returns `409` with the existing org id so the caller can switch to a join-request flow (`POST /api/organizations/:orgId/join-requests`) instead of trying to register a duplicate.

Tier transitions happen via the billing flow only — there is no `membership_tier` field on this endpoint. After org creation, send the user to `POST /api/checkout-session` (or the dashboard `/membership` page) to start a subscription; the Stripe webhook is the sole writer of `organizations.membership_tier`.

Rate-limited per user: `15` failed attempts per hour; successful calls do not count against the limit so a legitimate registration is never penalized by earlier validation errors.



## OpenAPI

````yaml /static/openapi/registry.yaml post /api/organizations
openapi: 3.1.0
info:
  title: AgenticAdvertising.org Registry API
  description: >-
    REST API for the AgenticAdvertising.org registry. Resolve brands,

    discover properties, look up agents, and validate authorization in the

    AdCP ecosystem.


    Most endpoints are public and require no authentication. Endpoints marked

    with a lock icon accept either an organization API key or a user JWT

    obtained via the OAuth 2.1 flow — see
    [Authentication](https://agenticadvertising.org/docs/registry/index#authentication).


    **Base URL:** `https://agenticadvertising.org`
  version: 1.0.0
  contact:
    name: AgenticAdvertising.org
    url: https://agenticadvertising.org
servers:
  - url: https://agenticadvertising.org
    description: Production
security: []
tags:
  - name: Onboarding
    description: >-
      Explicitly bootstrap a third-party integration into the AAO registry. Most
      callers don't need this tag — `POST /api/me/agents` auto-creates the org
      (for fresh users) and the member profile (for first-time agent
      registration) without a separate round trip. Use `POST /api/organizations`
      only when you need to override the auto-derived org name / company_type /
      revenue_tier. Tier transitions happen via the billing flow only; the
      Stripe webhook is the sole writer of `organizations.membership_tier`.
  - name: Member Agents
    description: >-
      Register, list, update, and remove agents on the caller's organization
      member profile. Authenticated programmatic surface for CI / scripts that
      don't want to round-trip the full member profile.
  - name: Brand Resolution
    description: Resolve advertiser domains to canonical brand identities.
  - name: Property Resolution
    description: >-
      Resolve publisher domains to their property configurations and authorized
      agents.
  - name: Agent Discovery
    description: >-
      Browse the federated agent network, search agent inventory profiles,
      publisher index, and registry statistics.
  - name: Change Feed
    description: Poll cursor-based registry change events for local sync.
  - name: Lookups & Authorization
    description: >-
      Look up agents by domain or property, and validate ad-serving
      authorization.
  - name: Validation Tools
    description: >-
      Validate publisher adagents.json files and generate compliant
      configurations.
  - name: Community Mirrors
    description: >-
      Publish, fetch, list, and retire catalog-only adagents.json mirrors for
      platforms that have not adopted AdCP.
  - name: Search
    description: Cross-entity search across brands, publishers, agents, and properties.
  - name: Agent Probing
    description: >-
      Connect to live agents and inspect their capabilities, formats, and
      inventory.
  - name: Brand Discovery
    description: Discover and crawl brand.json files across domains.
  - name: Agent Compliance
    description: Agent compliance status, storyboard test results, and compliance history.
  - name: Policy Registry
    description: >-
      Browse, resolve, and contribute governance policies for campaign
      compliance.
paths:
  /api/organizations:
    post:
      tags:
        - Onboarding
      summary: Create or adopt my organization
      description: >-
        Bootstrap the caller's organization explicitly. Use this when the caller
        wants to control the organization name, `company_type`, `revenue_tier`,
        or `is_personal` flag before any agents are registered.


        **Most storefront-style integrations don't need this call** — `POST
        /api/me/agents` will auto-create an org for a fresh OAuth user
        (corporate or personal workspace based on the email domain) and surface
        `org_auto_created: true` in the response. Reach for `POST
        /api/organizations` only when the auto-derived defaults aren't
        acceptable.


        Three outcomes depending on the caller's state:


        - **Fresh create** (most common): a new WorkOS organization is created,
        the caller is added as `owner`, the corporate domain is recorded as
        email-verified, and ToS / privacy-policy acceptance is logged from the
        request context. Returns `{ success: true, organization: { id, name }
        }`.


        - **Prospect adoption**: an organization with the caller's email domain
        already exists as a `prospect` (the registry pre-recorded it from a
        brand crawl but no human had claimed it yet). The caller is promoted to
        `owner` of the existing record instead of forking a duplicate. Returns
        `{ id, name, adopted: true }`.


        - **Already-active conflict**: the org exists and is already claimed by
        another paying member or a previously joined user. Returns `409` with
        the existing org id so the caller can switch to a join-request flow
        (`POST /api/organizations/:orgId/join-requests`) instead of trying to
        register a duplicate.


        Tier transitions happen via the billing flow only — there is no
        `membership_tier` field on this endpoint. After org creation, send the
        user to `POST /api/checkout-session` (or the dashboard `/membership`
        page) to start a subscription; the Stripe webhook is the sole writer of
        `organizations.membership_tier`.


        Rate-limited per user: `15` failed attempts per hour; successful calls
        do not count against the limit so a legitimate registration is never
        penalized by earlier validation errors.
      operationId: createOrganization
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrganizationInput'
      responses:
        '200':
          description: >-
            Prospect adoption — an existing prospect organization for this
            domain was claimed by the caller. Body is `{ id, name, adopted: true
            }`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateOrganizationResponse'
        '201':
          description: >-
            New organization created. Body is `{ success: true, organization: {
            id, name } }`. The caller is the `owner`; the corporate domain is
            recorded as email-verified for downstream registry calls.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateOrganizationResponse'
        '400':
          description: >-
            One of:

            - `organization_name` missing or invalid

            - `company_type` / `revenue_tier` value not in the documented enum

            - caller is on a personal-email domain (gmail.com, yahoo.com, …) and
            is trying to register a corporate org — register `is_personal: true`
            instead

            - per-user organization cap reached (10 orgs per user)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Authentication required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            An active organization already exists for this caller's email
            domain. The body includes `existing_org_id` and `existing_org_name`;
            the caller should switch to the join-request flow rather than
            retrying.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: >-
            Rate limit exceeded — 15 failed attempts per hour per user.
            Successful calls do not count against the limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - bearerAuth: []
        - oauth2: []
components:
  schemas:
    CreateOrganizationInput:
      type: object
      properties:
        organization_name:
          type: string
          minLength: 1
          maxLength: 200
          description: >-
            Display name for the organization. Used both as the org row name and
            (when auto-bootstrapping a member profile via the first agent
            registration) as the profile's `display_name`.
          example: Acme Media
        is_personal:
          type: boolean
          default: false
          description: >-
            Set to `true` to create a personal workspace instead of a corporate
            organization. Personal workspaces skip corporate-domain
            verification, are limited to one per user, and cannot host the
            `company_*` membership tiers.
        company_type:
          $ref: '#/components/schemas/OrganizationCompanyType'
        revenue_tier:
          $ref: '#/components/schemas/OrganizationRevenueTier'
        marketing_opt_in:
          type: boolean
          default: false
          description: >-
            Whether the caller opted in to AAO marketing communications.
            Recorded once per user (not overwritten on subsequent calls).
            Independent of Terms-of-Service consent, which is recorded
            server-side from the request context.
      required:
        - organization_name
      description: >-
        Request body for `POST /api/organizations`.


        Bootstraps a WorkOS organization, mirrors the caller as `owner`, records
        the caller's ToS / privacy-policy acceptance, and (for non-personal
        orgs) inserts an email-verified record into `organization_domains` so
        subsequent registry calls can skip explicit domain-verification.


        Membership tier and corporate domain are *not* caller-supplied: the tier
        is set by the Stripe webhook on subscription events, and the corporate
        domain is derived from the authenticated user's email.
    CreateOrganizationResponse:
      type: object
      properties:
        success:
          type: boolean
        organization:
          type: object
          properties:
            id:
              type: string
              example: org_01HXZAB123
            name:
              type: string
              example: Acme Media
          required:
            - id
            - name
        id:
          type: string
          description: >-
            Set on the **prospect-adoption** path: when an org with the user's
            email domain already exists in a `prospect` state (i.e. the registry
            pre-recorded it from a brand crawl but no human had claimed it yet),
            this call adopts that org for the caller instead of creating a new
            one.
        name:
          type: string
        adopted:
          type: boolean
          description: >-
            `true` when the response is the prospect-adoption path. When `true`,
            no new WorkOS organization was created — the caller is now the owner
            of an existing prospect record.
      description: >-
        Response from `POST /api/organizations`. The body shape varies by path:
        a fresh creation returns `{ success: true, organization: { id, name }
        }`; a prospect adoption returns `{ id, name, adopted: true }` directly.
        Both paths are 2xx; downstream callers should treat any `2xx` as "the
        org now exists and you are an owner of it" and read whichever id is
        present.
    Error:
      type: object
      properties:
        error:
          type: string
      required:
        - error
    OrganizationCompanyType:
      type: string
      enum:
        - adtech
        - agency
        - brand
        - publisher
        - data
        - ai
        - other
      description: >-
        Coarse classification of the organization's role in the open ad
        ecosystem. Drives default verification badges and the member profile's
        display category.
    OrganizationRevenueTier:
      type: string
      enum:
        - under_1m
        - 1m_5m
        - 5m_50m
        - 50m_250m
        - 250m_1b
        - 1b_plus
      description: >-
        Annual revenue band, USD. Drives membership-tier eligibility for
        company-tier seats.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Bearer token in the `Authorization` header. Two token types are
        accepted:


        - **Organization API key** (`sk_...`) issued via the dashboard.
        Org-scoped, long-lived, for server-to-server use.

        - **User JWT** obtained via the OAuth 2.1 authorization code flow with
        PKCE. User-scoped, short-lived. Discover the authorization server at
        `/.well-known/oauth-authorization-server` and the protected-resource
        metadata at `/.well-known/oauth-protected-resource/api`.
    oauth2:
      type: oauth2
      description: >-
        OAuth 2.1 authorization code flow with PKCE. Users authenticate via
        AuthKit and clients receive a Bearer JWT that authorizes both the MCP
        endpoint and this REST API. Dynamic client registration is supported at
        `/register`.
      flows:
        authorizationCode:
          authorizationUrl: https://agenticadvertising.org/authorize
          tokenUrl: https://agenticadvertising.org/token
          refreshUrl: https://agenticadvertising.org/token
          scopes:
            openid: User identifier
            profile: User profile information
            email: User email address

````