> ## Documentation Index
> Fetch the complete documentation index at: https://docs.segmnt.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Get project usage

> Account totals and plan limits are in `GET /v1/billing/plan`.



## OpenAPI

````yaml /api/openapi.yaml get /v1/projects/{projectId}/usage
openapi: 3.1.0
info:
  title: segmnt API
  version: 0.6.1
  summary: >-
    Video hosting and signed HLS playback for course platforms, agencies and
    training companies.
  description: >
    First draft of the segmnt API contract (task A2). It is a contract for the
    MVP

    and will change while we build it.


    ## Authentication

    - **API key** (`apiKey`): `Authorization: Bearer <key>`. A key belongs to
    one
      project. It can only call routes under `/v1/projects/{projectId}` for that
      project. Send it only from your server, never from a browser.
    - **Dashboard session** (`session`): cookie set after SSO sign-in (Google,
    GitHub,
      Reddit, Apple). It can call every route for projects in the signed-in account.
      Account routes (projects, API keys, billing) accept only the session.
      The cookie is `HttpOnly; Secure; SameSite=Lax`. Requests with a body must be
      `application/json`; other content types get `415`.

    The SSO sign-in routes live under `/auth/*` (task A8): the browser is

    redirected to the provider and back, then the `segmnt_session` cookie

    authenticates the account routes.


    ## Rate limits

    Every `/v1/**` and `/auth/**` request is rate limited by client IP (task

    A17): `/v1/**` allows 1,200 requests per minute and `/auth/**` allows 60 per

    minute. `GET /health` and `OPTIONS` preflight are never counted. A blocked

    request gets `429` with a `Retry-After` header and `code: "rate_limited"`.


    ## Video lifecycle

    `uploading` → `processing` → `ready`, or `failed`. Our team can move a video
    to

    `held` (it does not play) or back to `ready`. Moving to `ready`, `failed`

    or `held` sends a webhook.

    A held or deleted video stops playing within about a minute, also for tokens

    issued before.


    ## Playback

    Videos never have a public URL. Your server asks for a playback token for
    one

    video and one verified domain. The token goes in the `token` query parameter

    of the master playlist URL:

    `https://test-cdn.segmnt.cloud/v/{videoId}/master.m3u8?token={token}`.

    The CDN rewrites every playlist it returns and adds the same `?token=` to

    each link inside, so a player only needs the master playlist URL.


    Free plans must play with `embedUrl` (our player, with our logo).

    `playlistUrl` is returned only on plans with `whiteLabel`.


    **Domain check.** The CDN takes the `Origin` header, or the origin of

    `Referer` when there is no `Origin`. It must be exactly `https://{domain}`,

    where `{domain}` is the token's domain:

    - scheme `https` only; no port;

    - host compared without case (`Courses.Example.com` =
    `courses.example.com`);

    - exact host only: subdomains do not match (`www.courses.example.com` needs
      its own verified domain).

    Requests from our embed page (on the CDN host) pass the domain check; the

    embed page only loads inside a frame on the token's domain

    (`Content-Security-Policy: frame-ancestors`).


    **Expiry.** A token is expired when the CDN clock is at or after

    `expiresAt`. There is no clock skew allowance. Note: browsers keep segments

    in their cache for 1 day and playlists for 30 seconds, so test expiry with a

    fresh client or no browser cache.


    **CDN errors.** Every refused request has the header

    `X-Segmnt-Error: <code>` and an `application/problem+json` body with the
    same

    `code`. A token check runs in this order; the first failure wins:


    | Status | `code` | When |

    |---|---|---|

    | `404` | `not_found` | Path is not a video file we serve. |

    | `403` | `token_missing` | No `token` query parameter. |

    | `403` | `token_invalid` | Bad format or signature, or the token is for
    another video. |

    | `403` | `token_expired` | Token is past `expiresAt`. |

    | `403` | `origin_missing` | No `Origin` and no `Referer` header. |

    | `403` | `domain_mismatch` | `Origin` / `Referer` is not
    `https://{domain}`. |

    | `403` | `video_unavailable` | Video is held or deleted. |

    | `403` | `plan_limit_reached` | The account is over its free limit, or the
    request is for a quality above the plan's maximum. |


    A held or deleted video gets `video_unavailable` within about a minute, also
    for

    tokens issued before. After a delete, files are removed and the CDN can also

    answer `404` `not_found`. A request for a quality above your plan's maximum

    is also `plan_limit_reached`, even when the account is inside its limit; the

    master playlist only lists the qualities your plan allows.


    ## Conventions

    - IDs are strings with a type prefix: `acc_`, `usr_`, `prj_`, `vid_`,
    `upl_`, `key_`, `dom_`, `whe_`, `evt_`.

    - Times are RFC 3339 in UTC.

    - Lists use cursor pagination: pass `nextCursor` back as `cursor`.

    - Errors use `application/problem+json` (RFC 9457) with a stable `code`.

    - `Idempotency-Key` is scoped to the account and the route. The same key
    with
      a different body gets `422`.
    - Money is an integer in the smallest unit of the currency (halalas for
    SAR).
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
  contact:
    name: segmnt engineering
servers:
  - url: https://test-api.segmnt.cloud
    description: TEST environment
security:
  - apiKey: []
  - session: []
tags:
  - name: Auth
    description: |
      SSO sign-in for the dashboard. No passwords are stored. Sign-in is a
      browser redirect flow: `/auth/{provider}/start` → the provider →
      `/auth/{provider}/callback`, which sets the `segmnt_session` cookie.
      Launch providers: Google and GitHub. Apple and Reddit come later.
  - name: Projects
    description: An account has many projects. Agencies use one project per client.
  - name: API keys
    description: Server-side keys. Each key belongs to one project.
  - name: Domains
    description: >-
      Sites allowed to play the project's videos. A domain must be verified with
      a DNS TXT record.
  - name: Uploads
    description: Direct upload from the client to storage with pre-signed multipart URLs.
  - name: Videos
    description: Uploaded videos and their processing status.
  - name: Playback
    description: Signed playback tokens.
  - name: Webhooks
    description: >
      HTTPS endpoints that receive video events. The MVP has no "send test
      event"

      route and no delivery log. To test, register your own receiver and upload

      a short video.
  - name: Usage
    description: Stored and watched time.
  - name: Billing
    description: Plans and checkout through the payment provider's hosted page.
  - name: Admin
    description: |
      Internal routes for our team (A12 abuse/review and A13 bans). Not for
      customers. The page is at `/admin` behind Cloudflare Access. Every admin
      route is gated by `adminAccess` (a Cloudflare Access JWT, or the TEST
      `ADMIN_TOKEN`). Internal helper routes are called by our own Workers with
      `internalSecret`.
    x-internal: true
  - name: System
    description: Health check.
paths:
  /v1/projects/{projectId}/usage:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
    get:
      tags:
        - Usage
      summary: Get project usage
      description: Account totals and plan limits are in `GET /v1/billing/plan`.
      operationId: getProjectUsage
      parameters:
        - name: from
          in: query
          required: false
          description: >-
            Start of the period (inclusive). Default is the start of the current
            billing period.
          schema:
            type: string
            format: date
        - name: to
          in: query
          required: false
          description: End of the period (inclusive). Default is today.
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Usage of the project.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectUsage'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  parameters:
    ProjectId:
      name: projectId
      in: path
      required: true
      schema:
        type: string
        pattern: ^prj_[A-Za-z0-9]+$
  schemas:
    ProjectUsage:
      type: object
      required:
        - projectId
        - from
        - to
        - storedSeconds
        - watchedMinutes
      properties:
        projectId:
          type: string
        from:
          type: string
          format: date
        to:
          type: string
          format: date
        storedSeconds:
          type: integer
          description: |
            Total stored length of the project's videos, weighted by quality
            (1 h of 1440p counts as 2 h, 1 h of 4K counts as 4 h), which is what
            counts against the plan's stored-hours limit.
        watchedMinutes:
          type: integer
          description: Minutes watched by viewers in the period.
    Problem:
      type: object
      required:
        - type
        - title
        - status
        - code
      properties:
        type:
          type: string
          format: uri-reference
        title:
          type: string
        status:
          type: integer
        code:
          type: string
          description: Stable error code for programs.
          examples:
            - plan_limit_reached
        detail:
          type: string
  responses:
    BadRequest:
      description: The request is not valid.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Unauthorized:
      description: Missing or wrong API key or session.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: Not found, or not in a project this key or session can use.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: Project API key. Server-side only.
    session:
      type: apiKey
      in: cookie
      name: segmnt_session
      description: Dashboard session cookie, set after SSO sign-in.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.