Easy2257 Productions API

Query documentation status and verify RS256 compliance certificates for professionally produced content.

Operations 5

GET /api/v1/productions List productions by performer email #
GET /api/v1/productions/{productionId} Get production status #
POST /api/v1/productions/{productionId}/verify-certificate Verify a compliance certificate #
GET /api/v1/productions/{productionId}/certificates List a production's certificates #
GET /api/v1/productions/revoked-certificates Certificate revocation list #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/easy2257-com:easy2257-com-productions-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

easy2257-com-productions-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Easy2257 Partner Productions API
  version: '2026-07-27'
  description: 'The Easy2257 Partner API lets your platform outsource 18 U.S.C. § 2257 compliance in a single integration: performer ID verification, per-depiction content logging, and 7-year record retention, with Easy2257 named Custodian of Records. No endpoint in this API returns a performer''s legal name, date of birth, government ID image, or Easy2257 user id. A platform integrating it never touches a government ID, so it cannot leak one. There is no sandbox: only `ez_live_` keys are accepted, and every call writes a production record.'
  contact:
    name: Easy2257 Developer Support
    email: api@easy2257.com
    url: https://docs.easy2257.com
servers:
- url: https://easy2257.com
  description: Production, and the only environment. There is no sandbox host.
tags:
- name: Productions
  description: Query documentation status and verify RS256 compliance certificates for professionally produced content.
paths:
  /api/v1/productions:
    get:
      summary: List productions by performer email
      description: 'Every production a given performer is documented in, scoped to productions linked to your partner account. This is the right-to-removal lookup: when a performer asks to be removed, one call gives you every piece of content on your platform they appear in.'
      tags:
      - Productions
      security:
      - BearerAuth: []
      parameters:
      - schema:
          type: string
          format: email
          description: Required. Matched case-insensitively.
          example: performer@example.com
        required: true
        name: performerEmail
        in: query
      - schema:
          type: string
          enum:
          - pending_documentation
          - documented
          - revoked
          - takedown_pending
          - takedown_complete
          description: Filter by documentation status. Defaults to `documented`, so pass an explicit value to see anything else.
        required: false
        name: status
        in: query
      - schema:
          type: string
          description: 1 to 200. Default 50.
          example: '50'
        required: false
        name: limit
        in: query
      - schema:
          type: string
          description: '`nextCursor` from the previous page.'
        required: false
        name: cursor
        in: query
      responses:
        '200':
          description: Production list. An empty array means no match, which is not an error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductionListResponse'
        '400':
          description: '`performerEmail` missing. `code` is `missing_parameter`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'No `Authorization: Bearer` header, or a key that is unknown, revoked, or expired. The body carries only `error`, never a `code`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Rejected before the handler: a test-mode key (`TEST_MODE_UNAVAILABLE`) or a partner account that is not ACTIVE. Never used for cross-partner access, which returns 404.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'Rate limit exceeded: 300 requests per 60 seconds per API key, shared across all `/api/v1` endpoints (requests without a `Bearer` key fall back to 60 per 60 seconds per client IP, and a per-address flood ceiling of 1200 per 60 seconds also applies). Honor `Retry-After`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Server error. Retry with exponential backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: getApiV1Productions
      x-operation-id-source: derived
  /api/v1/productions/{productionId}:
    get:
      summary: Get production status
      description: 'Current compliance status of one production, plus the custodian identity you can print on a records statement. Scoped to productions linked to your partner account: anything else returns 404.'
      tags:
      - Productions
      security:
      - BearerAuth: []
      parameters:
      - schema:
          type: string
          example: cmd3p8w1k0002qz8h9j4r7v5s
        required: true
        name: productionId
        in: path
      responses:
        '200':
          description: Production found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductionResponse'
        '400':
          description: 'Invalid request: a required field is missing or malformed.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'No `Authorization: Bearer` header, or a key that is unknown, revoked, or expired. The body carries only `error`, never a `code`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Rejected before the handler: a test-mode key (`TEST_MODE_UNAVAILABLE`) or a partner account that is not ACTIVE. Never used for cross-partner access, which returns 404.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such production, or it is not linked to your partner account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'Rate limit exceeded: 300 requests per 60 seconds per API key, shared across all `/api/v1` endpoints (requests without a `Bearer` key fall back to 60 per 60 seconds per client IP, and a per-address flood ceiling of 1200 per 60 seconds also applies). Honor `Retry-After`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Server error. Retry with exponential backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: getApiV1ProductionsByProductionId
      x-operation-id-source: derived
  /api/v1/productions/{productionId}/verify-certificate:
    post:
      summary: Verify a compliance certificate
      description: 'Validates a stored JWT compliance certificate against Easy2257''s records: RS256 signature, expiry, revocation, and that the certificate belongs to the production in the URL. Use it for a nightly sweep or a dashboard check. For real-time signals prefer the `production.takedown_requested` webhook and the daily revocation list. For an offline check with no API call at all, verify the JWT against the public JWKS at `https://easy2257.com/.well-known/jwks.json`.'
      tags:
      - Productions
      security:
      - BearerAuth: []
      parameters:
      - schema:
          type: string
          example: cmd3p8w1k0002qz8h9j4r7v5s
        required: true
        name: productionId
        in: path
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerifyCertificateBody'
      responses:
        '200':
          description: 'The check ran. Read `valid`: this status is returned for good and bad certificates alike.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerifyCertificateResponse'
        '400':
          description: '`certificate` missing (`missing_field`), or the certificate verified but belongs to a different production (`production_id_mismatch`, returned as `{ valid: false, reason }`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'No `Authorization: Bearer` header, or a key that is unknown, revoked, or expired. The body carries only `error`, never a `code`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Rejected before the handler: a test-mode key (`TEST_MODE_UNAVAILABLE`) or a partner account that is not ACTIVE. Never used for cross-partner access, which returns 404.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'Rate limit exceeded: 300 requests per 60 seconds per API key, shared across all `/api/v1` endpoints (requests without a `Bearer` key fall back to 60 per 60 seconds per client IP, and a per-address flood ceiling of 1200 per 60 seconds also applies). Honor `Retry-After`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Server error. Retry with exponential backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: postApiV1ProductionsByProductionIdVerifyCertificate
      x-operation-id-source: derived
  /api/v1/productions/{productionId}/certificates:
    get:
      summary: List a production's certificates
      description: 'Every certificate minted for a linked production, newest first, with lifecycle status and — for certificates issued since storage began — the signed JWT itself. Use it to re-fetch a certificate you missed or lost, to pick up the replacement after a renewal, and to see at a glance which certificate is `CURRENT`. Certificates issued before storage began return `jwt: null`; those arrive with the next annual renewal. Scoped to productions linked to your partner account: anything else returns 404.'
      tags:
      - Productions
      security:
      - BearerAuth: []
      parameters:
      - schema:
          type: string
          example: cmd3p8w1k0002qz8h9j4r7v5s
        required: true
        name: productionId
        in: path
      responses:
        '200':
          description: Certificate list, newest first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CertificatesResponse'
        '400':
          description: 'Invalid request: a required field is missing or malformed.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'No `Authorization: Bearer` header, or a key that is unknown, revoked, or expired. The body carries only `error`, never a `code`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Rejected before the handler: a test-mode key (`TEST_MODE_UNAVAILABLE`) or a partner account that is not ACTIVE. Never used for cross-partner access, which returns 404.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such production, or it is not linked to your partner account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'Rate limit exceeded: 300 requests per 60 seconds per API key, shared across all `/api/v1` endpoints (requests without a `Bearer` key fall back to 60 per 60 seconds per client IP, and a per-address flood ceiling of 1200 per 60 seconds also applies). Honor `Retry-After`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Server error. Retry with exponential backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: getApiV1ProductionsByProductionIdCertificates
      x-operation-id-source: derived
  /api/v1/productions/revoked-certificates:
    get:
      summary: Certificate revocation list
      description: 'Certificates revoked since a timestamp, scoped to productions linked to your partner account. Run it on a daily cron. Every entry is a takedown signal: a reason starting `removal_request:` means take the content down. Annual renewals are not revocations and no longer enter this list (entries recorded before that split carry `type: supersession` and their content stays up). Advance your watermark from the top-level `retrievedAt`, and page with `nextCursor`.'
      tags:
      - Productions
      security:
      - BearerAuth: []
      parameters:
      - schema:
          type: string
          format: date-time
          description: ISO 8601. Defaults to 30 days ago. Use the `retrievedAt` from your last successful sync.
          example: '2026-04-01T00:00:00Z'
        required: false
        name: since
        in: query
      - schema:
          type: string
          description: 1 to 10000. Default 1000.
          example: '1000'
        required: false
        name: limit
        in: query
      - schema:
          type: string
          description: '`nextCursor` from the previous page, passed back verbatim.'
        required: false
        name: cursor
        in: query
      responses:
        '200':
          description: Revocation list.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CrlResponse'
        '400':
          description: '`since` is not a valid date. `code` is `invalid_parameter`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: 'No `Authorization: Bearer` header, or a key that is unknown, revoked, or expired. The body carries only `error`, never a `code`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: 'Rejected before the handler: a test-mode key (`TEST_MODE_UNAVAILABLE`) or a partner account that is not ACTIVE. Never used for cross-partner access, which returns 404.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: 'Rate limit exceeded: 300 requests per 60 seconds per API key, shared across all `/api/v1` endpoints (requests without a `Bearer` key fall back to 60 per 60 seconds per client IP, and a per-address flood ceiling of 1200 per 60 seconds also applies). Honor `Retry-After`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RateLimitError'
        '500':
          description: Server error. Retry with exponential backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      operationId: getApiV1ProductionsRevokedCertificates
      x-operation-id-source: derived
components:
  schemas:
    RateLimitError:
      type: object
      properties:
        error:
          type: string
          example: Too many requests. Please slow down.
        retryAfter:
          type: integer
          description: Seconds until the window frees up. Mirrors the `Retry-After` header.
          example: 14
        limit:
          type: integer
          description: Requests allowed per window.
          example: 60
        remaining:
          type: integer
          example: 0
        reset:
          type: integer
          description: Epoch milliseconds at which the sliding window releases the oldest request.
          example: 1714000060000
      required:
      - error
      - limit
      - remaining
      - reset
    CertificatesResponse:
      type: object
      properties:
        productionId:
          type: string
          example: cmd3p8w1k0002qz8h9j4r7v5s
        retrievedAt:
          type: string
          format: date-time
          example: '2026-08-15T18:00:00Z'
        certificates:
          type: array
          items:
            type: object
            properties:
              jwtId:
                type: string
                description: The certificate's `jti` claim — its stable identifier across this API.
                example: 0b4b8f4e-6a7c-4a1e-9f0e-2f1d7c3a5b9e
              kid:
                type: string
                description: Signing key id, resolvable against the public JWKS.
                example: ez2257-ad88852c
              issuedAt:
                type: string
                format: date-time
                example: '2026-08-15T18:00:00Z'
              expiresAt:
                type: string
                format: date-time
                example: '2027-08-15T18:00:00Z'
              status:
                type: string
                enum:
                - CURRENT
                - SUPERSEDED
                - REVOKED
                description: Lifecycle state. `CURRENT` is the one live certificate. `SUPERSEDED` means an annual renewal replaced it; the content stays up. `REVOKED` means the certified record was pulled; take the content down.
              revokedAt:
                type:
                - string
                - 'null'
                format: date-time
                description: Set on revoked certificates. Also present on certificates recorded as superseded before supersession was split out of revocation; `status` is the field to branch on.
              supersededAt:
                type:
                - string
                - 'null'
                format: date-time
                description: Set when an annual renewal replaced this certificate. Null on records that predate the supersession split; `status` is the field to branch on.
              supersededByJwtId:
                type:
                - string
                - 'null'
                description: '`jwtId` of the replacing certificate, when superseded.'
              jwt:
                type:
                - string
                - 'null'
                description: The signed certificate itself. `null` on certificates issued before certificates were stored; those were delivered once in their webhook and arrive again with the next annual renewal.
            required:
            - jwtId
            - kid
            - issuedAt
            - expiresAt
            - status
            - revokedAt
            - supersededAt
            - supersededByJwtId
            - jwt
          description: Every certificate minted for the production, newest first.
      required:
      - productionId
      - retrievedAt
      - certificates
    ProductionListResponse:
      type: object
      properties:
        productions:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
                example: cmd3p8w1k0002qz8h9j4r7v5s
              sceneTitle:
                type:
                - string
                - 'null'
                description: The production title.
                example: April 2026 Scene
              status:
                type: string
                enum:
                - pending_documentation
                - documented
                - revoked
                - takedown_pending
                - takedown_complete
                description: '`pending_documentation`: records not yet complete. `documented`: records on file and a certificate issued. `revoked`: certificate revoked by an actioned takedown. `takedown_pending`: removal request received, under review. `takedown_complete`: content removed, records sealed.'
              documentedAt:
                type:
                - string
                - 'null'
                format: date-time
            required:
            - id
            - sceneTitle
            - status
            - documentedAt
        nextCursor:
          type:
          - string
          - 'null'
          description: Pass back verbatim as `cursor`. null on the last page.
          example: cmd3p8w1k0002qz8h9j4r7v5s
      required:
      - productions
      - nextCursor
    VerifyCertificateBody:
      type: object
      properties:
        certificate:
          type: string
          description: The RS256 JWT compliance certificate Easy2257 issued, as delivered in the `production.documented` webhook.
          example: eyJhbGciOiJSUzI1NiIsImtpZCI6ImV6MjI1Ny1hZDg4ODUyYyJ9...
      required:
      - certificate
    CrlResponse:
      type: object
      properties:
        retrievedAt:
          type: string
          format: date-time
          description: 'Server time at which this response was built. This is the watermark: pass it as `since` on your next sync. There is no top-level `revokedAt`, and the per-item `revokedAt` values belong to individual certificates, so advancing a watermark off one of those will make your sync stall or skip.'
          example: '2026-04-24T18:00:00Z'
        revocations:
          type: array
          items:
            type: object
            properties:
              productionId:
                type: string
                example: cmd3p8w1k0002qz8h9j4r7v5s
              revokedAt:
                type: string
                format: date-time
                description: When this certificate was revoked.
                example: '2026-04-24T18:00:00Z'
              reason:
                type: string
                description: Free-form string, not a fixed enum. `removal_request:<id>` means an admin actioned a removal request, so pull the content. Match that one on the `removal_request:` prefix, since the id varies. `unspecified` means no reason was recorded. `annual_renewal` appears only on entries recorded before renewals were split out of this feed; the content stays up for those.
                example: removal_request:cmremreq0000000000000001
              type:
                type: string
                enum:
                - revocation
                - supersession
                description: 'What the entry is. `revocation` means the certified record was pulled: take the content down. `supersession` means an annual renewal replaced the certificate and the content stays up; it appears only on entries recorded before renewals stopped being written to this feed. New renewals never enter the list — they arrive as `production.certificate_renewed` webhooks and in `GET /api/v1/productions/{productionId}/certificates`.'
                example: revocation
            required:
            - productionId
            - revokedAt
            - reason
            - type
        nextCursor:
          type:
          - string
          - 'null'
          description: Opaque page cursor derived from the certificate record's `jwtId`. Pass it back verbatim as `cursor`. It is not a `productionId`, and ordering is stable on (revokedAt, jwtId) so pages never skip or repeat. null on the last page.
          example: 0b4b8f4e-6a7c-4a1e-9f0e-2f1d7c3a5b9e
      required:
      - retrievedAt
      - revocations
      - nextCursor
    ProductionResponse:
      type: object
      properties:
        id:
          type: string
          example: cmd3p8w1k0002qz8h9j4r7v5s
        status:
          type: string
          enum:
          - pending_documentation
          - documented
          - revoked
          - takedown_pending
          - takedown_complete
          description: '`pending_documentation`: records not yet complete. `documented`: records on file and a certificate issued. `revoked`: certificate revoked by an actioned takedown. `takedown_pending`: removal request received, under review. `takedown_complete`: content removed, records sealed.'
        performerCount:
          type: integer
          description: Number of talent participants across the production's scenes.
          example: 3
        documentedAt:
          type:
          - string
          - 'null'
          format: date-time
          example: '2026-04-24T18:00:00Z'
        custodianName:
          type: string
          description: Custodian of Records legal entity, exactly as published on the Easy2257 records statement. Render it, do not parse it.
        custodianAddress:
          type: string
          description: Custodian mailing address on one line. Empty string when no address is on file.
        takedownContact:
          type: string
          format: email
          example: takedown@easy2257.com
        lastVerifiedAt:
          type: string
          format: date-time
          description: Timestamp of this lookup, not of a stored verification event. Useful as an audit breadcrumb for when you last checked.
      required:
      - id
      - status
      - performerCount
      - documentedAt
      - custodianName
      - custodianAddress
      - takedownContact
      - lastVerifiedAt
    Error:
      type: object
      properties:
        error:
          type: string
          description: 'Human-readable message. Switch on HTTP status and on `code`, never on this string: it is not part of the API contract.'
          example: email, externalUserId, and callbackUrl are required
        code:
          type: string
          description: Machine-readable error code. Present on the test-key rejection (`TEST_MODE_UNAVAILABLE`), consent conflicts, and most collab-scene and upload-session validation errors. Never present on `401` authentication rejections, and absent on the inactive-partner `403` and on some older solo-account validation errors, which is why status is the primary signal.
          example: missing_parameter
        field:
          type: string
          description: The offending request field. Present on collab-scene and upload-session validation errors.
          example: contentType
      required:
      - error
    VerifyCertificateResponse:
      type: object
      properties:
        valid:
          type: boolean
          description: The only field you should branch on. A 200 response says the check ran, not that the certificate is good.
        productionId:
          type: string
          example: cmd3p8w1k0002qz8h9j4r7v5s
        issuedAt:
          type: string
          format: date-time
          description: Present when valid is true.
        expiresAt:
          type: string
          format: date-time
          description: Present when valid is true.
        currentStatus:
          type: string
          description: Current production documentation status. Present when valid is true. `unknown` if the production row has since been removed.
          example: documented
        reason:
          type: string
          enum:
          - certificate_revoked
          - certificate_superseded
          - certificate_expired
          - certificate_not_found
          - signature_mismatch
          - invalid_certificate
          - no_public_key
          - public_key_not_configured
          - production_id_mismatch
          description: 'Machine-readable failure reason, present when valid is false. `certificate_superseded` means a newer certificate replaced this one, either at annual renewal or when the producer reissued the sealed package after a correction: swap in the replacement (from the `production.certificate_renewed` webhook, the newer `production.documented` delivery, or `GET /api/v1/productions/{productionId}/certificates`); the content stays up. `certificate_revoked` means the certified record was pulled: take the content down. `production_id_mismatch` arrives with HTTP `400`: the certificate verified, but it belongs to a different production than the one in the URL. `no_public_key` and `public_key_not_configured` are Easy2257-side signing key problems, so retry rather than treating the certificate as bad.'
      required:
      - valid
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: 'Partner API key from partner.easy2257.com/api-keys. Only `ez_live_` keys are accepted. A key beginning with `ez_test_` is rejected with `403` and code `TEST_MODE_UNAVAILABLE` before any handler runs: there is no sandbox, and every handler writes production records, provisions real users, and sends real email. New partner accounts start with a test key, so create a live key before your first call.'