openapi: 3.1.0
info:
  title: BazaarLink API
  version: 1.0.0
  description: "OpenAI-compatible AI model relay for Taiwan. Base URL
    https://bazaarlink.ai/api/v1 for OpenAI-shaped endpoints. Full prose
    documentation: https://bazaarlink.ai/docs/api (markdown mirror:
    https://bazaarlink.ai/docs/api.md)."
  contact:
    url: https://bazaarlink.ai/contact
servers:
  - url: https://bazaarlink.ai
    description: Production
paths:
  /api/v1/chat/completions:
    post:
      operationId: postApiV1ChatCompletions
      summary: Chat Completions
      description: Send a chat completion request to any supported model via a single
        endpoint. Pass an OpenAI-shaped request body — messages, temperature,
        stream, tools, response_format are all supported. BazaarLink routes to
        the underlying provider (OpenAI, Anthropic, Google, DeepSeek, Meta, and
        more) transparently. Use reserved model IDs like auto:free to route to
        the cheapest available free model, or pass a models array for fallback
        routing.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody:
        &a1
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/chat/completions \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "openai/gpt-4o",
                "messages": [{"role": "user", "content": "Hello"}]
              }'
        - lang: python
          label: Python (OpenAI SDK)
          source: |-
            from openai import OpenAI
            import os

            client = OpenAI(
                api_key=os.environ["BAZAARLINK_API_KEY"],
                base_url="https://bazaarlink.ai/api/v1",
            )

            resp = client.chat.completions.create(
                model="openai/gpt-4o",
                messages=[{"role": "user", "content": "Hello"}],
            )
            print(resp.choices[0].message.content)
  /api/v1/embeddings:
    post:
      operationId: postApiV1Embeddings
      summary: Embeddings
      description: Generate dense vector embeddings for text using OpenAI, Voyage,
        Cohere, or other embedding model providers via a single unified
        interface. Request and response shapes are identical to OpenAI's
        embeddings API, so drop-in replacement with existing SDKs is
        straightforward. Supports single string or array-of-strings input.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/embeddings \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "openai/text-embedding-3-large",
                "input": "The quick brown fox"
              }'
        - lang: python
          label: Python (OpenAI SDK)
          source: |-
            from openai import OpenAI
            import os

            client = OpenAI(
                api_key=os.environ["BAZAARLINK_API_KEY"],
                base_url="https://bazaarlink.ai/api/v1",
            )

            resp = client.embeddings.create(
                model="openai/text-embedding-3-large",
                input="The quick brown fox",
            )
            print(resp.data[0].embedding[:8])
  /api/v1/models:
    get:
      operationId: getApiV1Models
      summary: List Models
      description: "Lists every model available through BazaarLink with pricing
        (prompt + completion per million tokens), maximum context length,
        supported modalities (text, vision, audio), and provider routing
        metadata. The response is OpenAI-shaped (data: array of model objects)
        with additional BazaarLink-specific fields. Use this endpoint to
        discover available models programmatically."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: curl https://bazaarlink.ai/api/v1/models
        - lang: typescript
          label: TypeScript (fetch)
          source: |-
            const res = await fetch("https://bazaarlink.ai/api/v1/models");
            const { data } = await res.json();
            console.log(`${data.length} models available`);
  /api/v1/models/:slug:
    get:
      operationId: getApiV1ModelsSlug
      summary: Get a Model
      description: Looks up one model by its URL slug (the canonical id with "/"
        replaced by "-", e.g. openai/gpt-4o -> openai-gpt-4o — the same slug
        used by the model detail page). Works for any public model without auth;
        with an organization's Bearer key, a model outside that org's model
        allowlist answers 404 (not 403) so the response never reveals whether a
        disallowed model exists. A retired model answers 410 using the same
        model_retired error convention as other endpoints.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: curl https://bazaarlink.ai/api/v1/models/openai-gpt-4o
        - lang: typescript
          label: TypeScript (fetch)
          source: >-
            const res = await
            fetch("https://bazaarlink.ai/api/v1/models/openai-gpt-4o");

            const model = await res.json();

            console.log(model.pricing);
  /api/v1/agents/register:
    post:
      operationId: postApiV1AgentsRegister
      summary: Register Agent
      description: Public, unauthenticated endpoint for autonomous agents that don't
        have a key yet. Accepts only name (truncated to 100 chars) and
        description (currently accepted but not stored or used). Returns a fresh
        api_key plus credits, claim_token, upgrade_url, and referral_code — the
        agent can start calling auto:free immediately, and a human can later
        visit upgrade_url to claim and fund the account. Rate limited to one
        registration per IP per 24 hours.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/agents/register \
              -H "Content-Type: application/json" \
              -d '{
                "name": "research-agent",
                "description": "Long-running research assistant"
              }'
        - lang: typescript
          label: TypeScript (fetch)
          source: >-
            const res = await
            fetch("https://bazaarlink.ai/api/v1/agents/register", {
              method: "POST",
              headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ name: "research-agent" }),
            });

            const { api_key, credits, claim_token, upgrade_url } = await res.json();
  /api/v1/messages:
    post:
      operationId: postApiV1Messages
      summary: Anthropic Messages
      description: Mirrors Anthropic's /v1/messages API shape (system, messages,
        max_tokens, tools). Point the official Anthropic SDK at BazaarLink's
        base URL and existing code works unchanged. BazaarLink transparently
        translates to the chosen underlying provider if the model is not native
        Anthropic, preserving tool_use blocks and stop reasons.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/messages \
              -H "x-api-key: $BAZAARLINK_API_KEY" \
              -H "anthropic-version: 2023-06-01" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "anthropic/claude-sonnet-4.6",
                "max_tokens": 1024,
                "messages": [{"role": "user", "content": "Hello"}]
              }'
        - lang: python
          label: Python (Anthropic SDK)
          source: |-
            from anthropic import Anthropic
            import os

            client = Anthropic(
                api_key=os.environ["BAZAARLINK_API_KEY"],
                base_url="https://bazaarlink.ai/api/v1",
            )

            msg = client.messages.create(
                model="anthropic/claude-sonnet-4.6",
                max_tokens=1024,
                messages=[{"role": "user", "content": "Hello"}],
            )
            print(msg.content[0].text)
  /api/v1/images/generations:
    post:
      operationId: postApiV1ImagesGenerations
      summary: Image Generations
      description: Generate images from a text prompt using any supported image model.
        Request shape matches OpenAI's /v1/images/generations — prompt, model,
        n, size. BazaarLink always returns hosted URLs (via a signed proxy);
        response_format=b64_json is not supported on this endpoint.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/images/generations \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "openai/gpt-5.4-image-2",
                "prompt": "A serene mountain lake at sunset",
                "size": "1024x1024",
                "n": 1
              }'
        - lang: python
          label: Python (OpenAI SDK)
          source: |-
            from openai import OpenAI
            import os

            client = OpenAI(
                api_key=os.environ["BAZAARLINK_API_KEY"],
                base_url="https://bazaarlink.ai/api/v1",
            )

            img = client.images.generate(
                model="openai/gpt-5.4-image-2",
                prompt="A serene mountain lake at sunset",
                size="1024x1024",
            )
            print(img.data[0].url)
  /api/v1/images/edits:
    post:
      operationId: postApiV1ImagesEdits
      summary: Image Edits
      description: "Edit or transform existing images with a text prompt. Multipart
        adapter over the image-to-image pipeline: each uploaded `image` file (up
        to 8, 10 MB each) is passed as an input image alongside `prompt` and
        optional `model`. Response matches OpenAI's sync JSON shape ({ created,
        data: [{ url }] }) so `client.images.edit()` works unchanged. Not yet
        supported: `mask` and `response_format=b64_json` (URLs are always
        returned)."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/images/edits \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -F "image=@photo.png" \
              -F "model=gpt-5.4-image-2" \
              -F "prompt=Replace the sky with a starry night"
        - lang: python
          label: Python (OpenAI SDK)
          source: |-
            from openai import OpenAI
            import os

            client = OpenAI(
                api_key=os.environ["BAZAARLINK_API_KEY"],
                base_url="https://bazaarlink.ai/api/v1",
            )

            img = client.images.edit(
                model="gpt-5.4-image-2",
                image=open("photo.png", "rb"),
                prompt="Replace the sky with a starry night",
            )
            print(img.data[0].url)
  /api/v1/videos:
    post:
      operationId: postApiV1Videos
      summary: Submit Video Generation
      description: Submit an async video generation job. Returns a BazaarLink job id;
        poll GET /api/v1/videos/{id} until status=completed. Supports duration,
        resolution, aspect_ratio, size, frame_images, input_references,
        generate_audio, watermark, and seed.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/videos \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "bytedance/seedance-2.0",
                "prompt": "a cat walking through a field",
                "duration": 2,
                "resolution": "720p",
                "generate_audio": false
              }'
  /api/v1/videos/:id:
    get:
      operationId: getApiV1VideosId
      summary: Poll Video Generation Status
      description: Returns status (pending/in_progress/completed/failed). When
        completed, usage.cost and unsigned_urls are populated. Clients should
        poll with >=5s interval.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/videos/vjob_xxx \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/videos/:id/content:
    get:
      operationId: getApiV1VideosIdContent
      summary: Download Generated Video
      description: Stream the generated MP4 bytes. Query param ?index=0 selects which
        output when a job returns multiple.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/videos/vjob_xxx/content?index=0 \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              --output out.mp4
  /api/v1/video/generations:
    post:
      operationId: postApiV1VideoGenerations
      summary: Submit Video Generation (OpenAI-style)
      description: Identical behavior to POST /api/v1/videos — accepted to ease
        migration from OpenAI-style SDKs that namespace under
        /video/generations. Returns a BazaarLink job id; poll GET
        /api/v1/video/generations/:id (or /api/v1/videos/:id) until
        status=completed.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/video/generations \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "bytedance/seedance-2.0",
                "prompt": "a cat walking through a field",
                "duration": 2,
                "resolution": "720p",
                "generate_audio": false
              }'
  /api/v1/video/generations/:id:
    get:
      operationId: getApiV1VideoGenerationsId
      summary: Poll Video Generation Status (OpenAI-style)
      description: Identical behavior to GET /api/v1/videos/:id. Returns status
        (pending/in_progress/completed/failed); when completed, usage.cost and
        unsigned_urls are populated. Clients should poll with >=5s interval.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/video/generations/vjob_xxx \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/video/generations/:id/content:
    get:
      operationId: getApiV1VideoGenerationsIdContent
      summary: Download Generated Video (OpenAI-style)
      description: Identical behavior to GET /api/v1/videos/:id/content. Streams the
        generated MP4 bytes. Query param ?index=0 selects which output when a
        job returns multiple.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl
            https://bazaarlink.ai/api/v1/video/generations/vjob_xxx/content?index=0
            \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              --output out.mp4
  /api/v1/responses:
    post:
      operationId: postApiV1Responses
      summary: Responses
      description: "Mirrors OpenAI's /v1/responses API request/response shape (input,
        model, instructions, tools) with function tools, structured output, and
        reasoning passthrough. Stateless by design: sending store: true or
        previous_response_id returns HTTP 400 rather than silently ignoring them
        — send the full input array on every call."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/responses \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "openai/gpt-4o",
                "input": "What is the capital of France?"
              }'
        - lang: python
          label: Python (OpenAI SDK)
          source: |-
            from openai import OpenAI
            import os

            client = OpenAI(
                api_key=os.environ["BAZAARLINK_API_KEY"],
                base_url="https://bazaarlink.ai/api/v1",
            )

            resp = client.responses.create(
                model="openai/gpt-4o",
                input="What is the capital of France?",
            )
            print(resp.output_text)
  /api/v1/generation:
    get:
      operationId: getApiV1Generation
      summary: Generation Metadata
      description: After receiving a chat completion or streaming response, pass the
        generation ID returned in the response to retrieve authoritative usage
        and billing information. This is the best source for exact cost
        accounting because the streaming response may report pre-finalized token
        counts. Returns tokens, cost, model, provider, and timing.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl "https://bazaarlink.ai/api/v1/generation?id=gen_abc123" \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
        - lang: typescript
          label: TypeScript (fetch)
          source: |-
            const res = await fetch(
              `https://bazaarlink.ai/api/v1/generation?id=${generationId}`,
              { headers: { Authorization: `Bearer ${apiKey}` } },
            );
            const { data } = await res.json();
            console.log(data.cost_breakdown.total);
  /api/v1/key:
    get:
      operationId: getApiV1Key
      summary: Current Key Info
      description: Introspect the API key that authenticated the current request.
        Useful for clients that need to display their own quota or confirm which
        key is active. Returns label, spend limit and remaining budget, limit
        reset period, expiry, free-tier flag, and rate-limit tier.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/key \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/auth/key:
    get:
      operationId: getApiV1AuthKey
      summary: Auth Key Probe
      description: "Lightweight auth probe that confirms an API key is valid without
        consuming any token budget. Returns { data: { label, usage, limit,
        is_free_tier, rate_limit } } — a label and usage summary in the
        industry-conventional key-info shape, not a user/org/agent identity
        object. Use as a health check before issuing expensive completion
        calls."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/auth/key \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/credits:
    get:
      operationId: getApiV1Credits
      summary: Credit Balance
      description: Shows the current prepaid credit balance and lifetime usage for the
        authenticated user. Expressed in USD. Updates in near-real-time as
        completions settle.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/credits \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/usage:
    get:
      operationId: getApiV1Usage
      summary: Usage History
      description: "Paginated access to per-request usage history: timestamp, model,
        prompt tokens, completion tokens, cost, latency. Supports date-range and
        model filters via query parameters. Internal billing endpoint."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl
            "https://bazaarlink.ai/api/v1/usage?period=custom&from=2026-04-01&to=2026-04-13"
            \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/keys:
    get:
      operationId: getApiV1Keys
      summary: List API Keys
      description: Returns the set of API keys created by the authenticated user,
        including label, creation date, last-used timestamp, and usage-to-date.
        Key secrets are never returned — only a prefix hint.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/keys \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
    post:
      operationId: postApiV1Keys
      summary: Create API Key
      description: Creates a new API key under the authenticated account. The secret
        is returned exactly once in the creation response and cannot be
        retrieved later. Supports per-key spend limits and optional expiration
        date.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/keys \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"label": "production-worker", "limit": 100}'
  /api/v1/keys/:id:
    patch:
      operationId: patchApiV1KeysId
      summary: Update API Key
      description: Modifies an existing API key's enabled flag, spend limit
        (spendLimitUsd), or limit reset period (spendLimitPeriod). There is no
        rename operation — the label is set at creation and is immutable. The
        secret itself is also immutable; rotate by creating a new key and
        deleting the old one.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X PATCH https://bazaarlink.ai/api/v1/keys/KEY_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"enabled": true, "spendLimitUsd": 250, "spendLimitPeriod": "monthly"}'
    delete:
      operationId: deleteApiV1KeysId
      summary: Delete API Key
      description: Revokes an API key. The key becomes immediately invalid for new
        requests; already-authenticated in-flight completions finish normally.
        Usage history is retained for billing.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X DELETE https://bazaarlink.ai/api/v1/keys/KEY_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/management/byok-keys:
    get:
      operationId: getApiV1ManagementByokKeys
      summary: List BYOK Keys
      description: Returns every BYOK upstream key of the organization the management
        key is bound to, including protocol, fallback mode, per-model mappings,
        scoped platform keys, and health fields. The upstream key value only
        ever appears masked (keyMask) — the full value is never retrievable
        after creation.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/management/byok-keys \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
    post:
      operationId: postApiV1ManagementByokKeys
      summary: Create BYOK Key
      description: Registers an upstream provider key (OpenAI- or Anthropic-protocol)
        for the organization. The plaintext apiKey appears only in this request
        — encrypted at rest, masked thereafter. Private/local baseUrl addresses
        are rejected (400, SSRF guard); scopedApiKeyIds must reference keys the
        org owns. Answers 201 with the key object plus a sync result
        (matched/unmatched/stale).
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl https://bazaarlink.ai/api/v1/management/byok-keys \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"name": "team-upstream", "baseUrl": "https://api.example-upstream.com/v1", "apiKey": "'$UPSTREAM_API_KEY'"}'
  /api/v1/management/byok-keys/:id:
    get:
      operationId: getApiV1ManagementByokKeysId
      summary: Get BYOK Key
      description: Returns one BYOK key of the calling organization. A key that does
        not exist or belongs to another organization answers 404 in both cases —
        existence is never leaked.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/management/byok-keys/BYOK_KEY_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
    patch:
      operationId: patchApiV1ManagementByokKeysId
      summary: Update BYOK Key
      description: All fields optional. modelToggles flips routing per canonical model
        id (unlisted models keep their state); scopedApiKeyIds replaces the
        whole scope list (empty array restores org-wide applicability; foreign
        key ids answer 400). The upstream key value itself cannot be replaced —
        delete and recreate to rotate it.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X PATCH
            https://bazaarlink.ai/api/v1/management/byok-keys/BYOK_KEY_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"enabled": false}'
    delete:
      operationId: deleteApiV1ManagementByokKeysId
      summary: Delete BYOK Key
      description: Irreversible. Model mappings cascade-delete with the key; traffic
        that routed through this key falls back to platform routing afterwards.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X DELETE
            https://bazaarlink.ai/api/v1/management/byok-keys/BYOK_KEY_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/images/proxy/:token:
    get:
      operationId: getApiV1ImagesProxyToken
      summary: Image Proxy
      description: When /api/v1/images/generations returns a URL response_format,
        BazaarLink issues a signed token that fronts the underlying provider
        storage. This endpoint resolves the token, fetches the upstream image,
        and streams it back — keeping the provider URL private and enabling auth
        + caching.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: curl https://bazaarlink.ai/api/v1/images/proxy/SIGNED_TOKEN -o image.png
  /api/v1/images/models:
    get:
      operationId: getApiV1ImagesModels
      summary: List Image Generation Models
      description: Returns every image-output model with pricing, modalities, and
        supported_parameters. Use it to discover which models honor normalized
        image parameters (aspect_ratio, resolution, quality, …) natively and
        which accept input_references for image-to-image. No auth required;
        sending an organization API key filters the list by the org's model
        allowlist.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: curl https://bazaarlink.ai/api/v1/images/models
  /api/v1/orgs:
    get:
      operationId: getApiV1Orgs
      summary: List Organizations
      description: "Returns every organization the authenticated user is a member of,
        including role and joined date, plus each org's credit balance. Bearer
        auth only accepts a management-type key (keyType: \"management\"); a
        standard sk-bl-... key is rejected."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/orgs \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/orgs/:orgId:
    get:
      operationId: getApiV1OrgsOrgId
      summary: Get Organization
      description: "Returns full details for a specific organization, including
        billing plan, seat count, credit balance, and admin-configurable
        settings. Requires an org-admin role — a plain member's key gets 403. As
        with all /api/v1/orgs/* routes, Bearer auth only accepts a
        management-type key (keyType: \"management\"); a standard sk-bl-... key
        is rejected."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/orgs/ORG_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/orgs/:orgId/teams:
    get:
      operationId: getApiV1OrgsOrgIdTeams
      summary: List Organization Teams
      description: Returns the list of teams belonging to the specified organization,
        with metadata including team name, member count, and permission scopes.
        Requires an org-admin role and a management-type Bearer key (a standard
        sk-bl-... key is rejected).
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/orgs/ORG_ID/teams \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
    post:
      operationId: postApiV1OrgsOrgIdTeams
      summary: Create Organization Team
      description: Creates a team scoped to the organization. Teams partition members
        into permission groups for shared keys and spend limits. Requires an
        org-admin role and a management-type Bearer key.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/orgs/ORG_ID/teams \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"name": "ml-research"}'
  /api/v1/orgs/:orgId/teams/:teamId:
    patch:
      operationId: patchApiV1OrgsOrgIdTeamsTeamId
      summary: Update Organization Team
      description: Modifies metadata on an existing team — name, description, or
        permission scope. Does not change member list (use the members endpoints
        for that). Requires a management-type Bearer key.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X PATCH https://bazaarlink.ai/api/v1/orgs/ORG_ID/teams/TEAM_ID
            \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"name": "ml-research-renamed"}'
    delete:
      operationId: deleteApiV1OrgsOrgIdTeamsTeamId
      summary: Delete Organization Team
      description: Deletes a team. Members of the team remain members of the parent
        organization but lose any team-scoped keys or permissions. Requires an
        org-admin role and a management-type Bearer key.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X DELETE
            https://bazaarlink.ai/api/v1/orgs/ORG_ID/teams/TEAM_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/orgs/:orgId/members:
    get:
      operationId: getApiV1OrgsOrgIdMembers
      summary: List Organization Members
      description: Returns the full membership of an organization. Each entry includes
        user identity, role (owner / admin / member), join date, and
        usage-to-date. Requires an org-admin role and a management-type Bearer
        key (a standard sk-bl-... key is rejected).
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/orgs/ORG_ID/members \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
    post:
      operationId: postApiV1OrgsOrgIdMembers
      summary: Invite Organization Member
      description: Creates an invitation for a new org member. The invitee receives an
        email with a signed join link. Role is one of owner / admin / member and
        determines permission scope. Requires an org-admin role and a
        management-type Bearer key.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/orgs/ORG_ID/members \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"email": "colleague@example.com", "role": "member"}'
  /api/v1/orgs/:orgId/members/:memberId:
    patch:
      operationId: patchApiV1OrgsOrgIdMembersMemberId
      summary: Update Organization Member
      description: Changes the role (owner / admin / member) of an existing
        organization member. Used to promote or demote users. Cannot remove the
        last owner. Requires a management-type Bearer key.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X PATCH
            https://bazaarlink.ai/api/v1/orgs/ORG_ID/members/MEMBER_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"role": "admin"}'
    delete:
      operationId: deleteApiV1OrgsOrgIdMembersMemberId
      summary: Remove Organization Member
      description: Revokes a user's membership in an organization. Their personal
        account and personal API keys remain; they lose access to org-scoped
        resources immediately. Requires an org-admin role and a management-type
        Bearer key.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X DELETE
            https://bazaarlink.ai/api/v1/orgs/ORG_ID/members/MEMBER_ID \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
  /api/v1/cursor/chat/completions:
    post:
      operationId: postApiV1CursorChatCompletions
      summary: Cursor Chat Completions (Compat)
      description: Cursor appends /chat/completions to the configured Override URL, so
        users who set their base to .../v1/cursor land here. This route
        re-exports the same POST handler as /api/v1/chat/completions with
        identical behavior. All request/response shapes, streaming, and tool
        calling work identically. New integrations should use
        /api/v1/chat/completions directly.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl https://bazaarlink.ai/api/v1/cursor/chat/completions \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{"model": "openai/gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'
  /api/v1/audio/transcriptions:
    post:
      operationId: postApiV1AudioTranscriptions
      summary: Audio Transcriptions
      description: "Accepts audio input in two formats: multipart/form-data with a
        file field (OpenAI SDK compatible) or a JSON body with input_audio.data
        (base64) and input_audio.format (OR-native). Forwards to the upstream
        provider's /audio/transcriptions endpoint as JSON. Settles credits from
        usage.cost in the response. Supports language, response_format, prompt,
        and temperature fields."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL (file upload)
          source: |-
            curl https://bazaarlink.ai/api/v1/audio/transcriptions \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -F "file=@audio.mp3" \
              -F "model=mistralai/voxtral-small-24b-2507"
        - lang: python
          label: Python (OpenAI SDK)
          source: >-
            from openai import OpenAI

            import os


            client = OpenAI(api_key=os.environ["BAZAARLINK_API_KEY"], base_url="https://bazaarlink.ai/api/v1")

            with open("audio.mp3", "rb") as f:
                resp = client.audio.transcriptions.create(model="mistralai/voxtral-small-24b-2507", file=f)
            print(resp.text)
  /api/v1/audio/speech:
    post:
      operationId: postApiV1AudioSpeech
      summary: Audio Speech (Text-to-Speech)
      description: Generate speech from text. Accepts the OpenAI-compatible JSON body
        (model, input, voice, response_format, speed) and streams the binary
        audio response back unchanged. Billing is per input character using the
        model's catalog price (TTS providers return no usage envelope). Select a
        currently available speech-capable model from GET
        /api/v1/models?output_modalities=speech — the model below is a
        placeholder, not a fixed default.
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      requestBody: *a1
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl https://bazaarlink.ai/api/v1/audio/speech \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY" \
              -H "Content-Type: application/json" \
              -d '{
                "model": "YOUR_TTS_MODEL",
                "input": "Hello from BazaarLink!",
                "voice": "YOUR_VOICE"
              }' --output speech.mp3
        - lang: python
          label: Python (OpenAI SDK)
          source: >-
            from openai import OpenAI

            import os


            client = OpenAI(api_key=os.environ["BAZAARLINK_API_KEY"], base_url="https://bazaarlink.ai/api/v1")

            resp = client.audio.speech.create(
                model="YOUR_TTS_MODEL",
                voice="YOUR_VOICE",
                input="Hello from BazaarLink!",
            )

            resp.write_to_file("speech.mp3")
  /api/v1/insights:
    get:
      operationId: getApiV1Insights
      summary: Usage Insights
      description: "Returns aggregated usage insights for the authenticated user
        (session cookie or Bearer sk-bl-... key). Query params: period (7d
        default | 30d | ytd | all), heatmap_metric (spend default | tokens |
        requests), plus optional page-wide filters model, api_key_id, app.
        Responses are cached for 5 minutes per (user, period, metric, filters)
        combination. The userId is always derived from the verified caller — it
        can never be overridden via query string."
      tags:
        - v1
      responses:
        "200":
          description: Success
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
        "400":
          description: Invalid request
        "401":
          description: Missing or invalid API key
        "429":
          description: Rate limited
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl
            "https://bazaarlink.ai/api/v1/insights?period=30d&heatmap_metric=tokens"
            \
              -H "Authorization: Bearer $BAZAARLINK_API_KEY"
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: sk-bl-...
      description: "BazaarLink API key from https://bazaarlink.ai/dashboard — pass as
        `Authorization: Bearer sk-bl-...`."
