> ## 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.

# Rotate the signing secret of an endpoint

> Makes a new signing secret for the endpoint and returns it once. The
endpoint, its URL and its event list do not change. The previous secret
stops working as soon as this call returns, so update your server first.




## OpenAPI

````yaml /api/openapi.yaml post /v1/projects/{projectId}/webhook-endpoints/{endpointId}/rotate-secret
openapi: 3.1.0
info:
  title: segmnt API
  version: 0.16.1
  summary: >-
    Video infrastructure. Upload through one API and we encode, store, secure
    and stream it on your own domain.
  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://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`).


    **Viewer network (optional). Coming soon.** A project will be able to turn

    on `ipBinding` (default off). Then every new playback token is bound to the

    viewer's network (IPv4 `/24`, IPv6 `/64`) and the CDN will refuse a request

    from another network with `403 ip_mismatch`. Because our API sees your

    server's IP, not the viewer's, your server will pass the viewer's IP as

    `viewerIp` when it creates the token. Without `ipBinding` the field is

    ignored. This is not DRM: two viewers on the same `/24`, or behind one

    mobile-carrier NAT, can still share. The setting applies to tokens you

    create after you change it: tokens you created before the change keep

    working (or stay bound) until they expire. Use a short `expiresInSeconds`

    when you turn binding on.


    **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` | `ip_mismatch` | Coming soon. The token is bound to the viewer's
    network (project `ipBinding`) and this request is from another network (or
    the viewer IP cannot be checked). |

    | `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://api.segmnt.cloud
    description: Production
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: Upload tokens
    description: |
      Short-lived, single-use tokens for uploading from a browser. Your server
      mints one with the project API key; the browser uses it for exactly one
      upload and never sees the key.
  - name: Videos
    description: Uploaded videos and their processing status.
  - name: Playback
    description: Signed playback tokens.
  - name: Demo
    description: |
      The public marketing `/demo` page. It fetches a fresh, short-lived
      playback token on load so the page holds no key and no long-lived token.
      No authentication; one protected demo video, fixed by configuration.
  - 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: 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 a shared
      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}/webhook-endpoints/{endpointId}/rotate-secret:
    parameters:
      - $ref: '#/components/parameters/ProjectId'
      - name: endpointId
        in: path
        required: true
        schema:
          type: string
          pattern: ^whe_[A-Za-z0-9]+$
    post:
      tags:
        - Webhooks
      summary: Rotate the signing secret of an endpoint
      description: |
        Makes a new signing secret for the endpoint and returns it once. The
        endpoint, its URL and its event list do not change. The previous secret
        stops working as soon as this call returns, so update your server first.
      operationId: rotateWebhookEndpointSecret
      responses:
        '200':
          description: New secret. `signingSecret` is shown only in this response.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookEndpointWithSecret'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  parameters:
    ProjectId:
      name: projectId
      in: path
      required: true
      schema:
        type: string
        pattern: ^prj_[A-Za-z0-9]+$
  schemas:
    WebhookEndpointWithSecret:
      allOf:
        - $ref: '#/components/schemas/WebhookEndpoint'
        - type: object
          required:
            - signingSecret
          properties:
            signingSecret:
              type: string
              description: Use it to check `Segmnt-Signature`.
    WebhookEndpoint:
      type: object
      required:
        - id
        - url
        - events
        - createdAt
      properties:
        id:
          type: string
          examples:
            - whe_5mJ8kV
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
        createdAt:
          $ref: '#/components/schemas/Timestamp'
    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
    WebhookEventType:
      type: string
      enum:
        - video.ready
        - video.failed
        - video.held
        - video.removed
    Timestamp:
      type: string
      format: date-time
  responses:
    Unauthorized:
      description: Missing or wrong API key or session.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Forbidden:
      description: |
        Not allowed. `code` says why: `insufficient_scope` (a playback-only API
        key called a route it may not use), a plan limit
        (`plan_limit_reached`), or a domain that is not verified
        (`domain_not_verified`).
      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.