Easy2257
API Reference

API Reference

Authentication, idempotency, errors, rate limits, and the full Easy2257 Partner API endpoint reference.

Easy2257 is a REST API over HTTPS. Every endpoint lives under https://easy2257.com/api/v1/. The endpoint reference is in the left nav; the conventions below hold across all of it.

What your platform never has to store

No endpoint in this API returns a performer's legal name, date of birth, government ID image, or Easy2257 user id. The only identifiers you hold are the accountId Easy2257 issues and the externalUserId you supplied yourself.

That is the point of naming Easy2257 as Custodian of Records. The ID material never lands in your database, so it cannot leak from it, it cannot be subpoenaed out of it, and there is nothing for you to produce when an inspector calls: the inspection goes to Easy2257.

Authentication

Send your API key as a Bearer token on every request:

Authorization: Bearer ez_live_abc123...
Key prefixResult
ez_live_Accepted.
ez_test_Rejected with 403 and code TEST_MODE_UNAVAILABLE, before any handler runs.

There is no sandbox and no sandbox host. Test-mode keys are refused outright because every v1 handler writes production records, provisions real users, and sends real email. The rejection body is:

{
  "error": "Test-mode keys cannot be used against the production API. A sandbox environment is not yet available; use your ez_live_ key.",
  "code": "TEST_MODE_UNAVAILABLE"
}

Rejected calls still land in your request log at partner.easy2257.com/api-usage, so a misconfigured key is visible rather than silent.

New partner accounts are issued a test key. Your first task is to create a live key at partner.easy2257.com/api-keys. A first integration that fails with 403 almost always has the default test key wired in.

The secret is shown once at creation and stored only as a SHA-256 hash, so there is no reveal later. Copy it straight into your secret store; if you lose it, rotate. Up to 10 active keys per partner.

To rotate without downtime: create the new key, deploy with it, confirm traffic has moved by watching lastUsedAt in the portal, then revoke the old one. A 24-hour overlap is conventional.

Idempotency

There is no Idempotency-Key header, and nothing reads one if you send it. Replay safety is a property of the resource instead, keyed on values you already send:

EndpointDeduplicated on
POST /api/v1/solo-accounts(your partner account, externalUserId) and (your partner account, email)
POST /api/v1/solo-accounts/{accountId}/contentexternalContentId, falling back to contentUrl when you omit it
DELETE /api/v1/solo-accounts/{accountId}/content/{externalContentId}The depiction itself. A second call returns { "deleted": false }.
POST /api/v1/collab-scenes/{sceneId}/extend(your partner account, sceneId). A re-POST retries an unpaid extension fee instead of charging again.
POST /api/v1/collab-scenes/{sceneId}/publications(your partner account, contentUrl). A repeat returns 409 / duplicate_publication.
POST /api/v1/collab-scenes/{sceneId}/publications/{pubId}/removeThe publication's removedAt. A repeat returns the original value.

Replays return the existing record with 200 instead of 201. Content-log replays also carry "duplicate": true.

Two consequences worth designing around. The email scope on provisioning means a creator's consent decision sticks even if your own user id for them changes. And a retry fired after a timeout cannot produce a second account, a second charge, or a second email.

Two endpoints are not idempotent: POST /api/v1/collab-scenes and POST /api/v1/upload-sessions. Each call creates a new scene or session, and the scene call also sends invitation emails and charges fees. Guard both behind your own dedupe key.

Errors

Errors are a flat envelope. The message lives in error, and the HTTP status is the contract:

{ "error": "email, externalUserId, and callbackUrl are required" }

Most endpoints add a machine-readable code. Authentication rejections do not: a 401 body carries only error, so switch on the status there.

The collab-scene and upload-session routes also add field:

{ "error": "detectedFaceCount must be an integer between 1 and 50", "code": "invalid_field", "field": "detectedFaceCount" }
HTTP statusMeaning
400Invalid request: missing or malformed parameter
401Missing Authorization header, or a key that is unknown, revoked, or expired
403Only three causes: a test-mode key (TEST_MODE_UNAVAILABLE), a partner account that is not active, or registering a publication on a scene you have not extended (extension_required)
404Not found, and the answer for anything belonging to another partner
409State conflict: CONSENT_PENDING, CONSENT_DECLINED, duplicate_publication, or uploader_not_solo_active on the content and upload-session routes (on POST /v1/collab-scenes that same code is a 400); collab_scenes_not_available_on_platform_pays on POST /v1/collab-scenes from a platform-funded account
422Understood, but the resource is in a state that does not allow it
429Rate limited
500Server error: retry with exponential backoff

Solo-account and production reads return 404, never 403, for a resource that is not yours. An accountId, its content, or a production belonging to another partner is indistinguishable from one that does not exist. That is deliberate: it means the API cannot be walked to discover which creators are on which platform, including yours. Collab scenes are the exception, because cross-partner syndication is a supported flow there rather than an error. POST /collab-scenes/{sceneId}/publications can return 403 with extension_required: the scene exists, but your partner account has not extended onto it yet. Call POST /extend first, and handle the 403 alongside 404.

Switch on status and on code. Message text is not part of the contract and can change.

Rate limits

300 requests per 60 seconds, keyed by API key, applied by edge middleware to every /api/v1 request that presents an API key in the Authorization: Bearer header. It is a sliding window, and all v1 endpoints share one bucket per key, so calls made with one key add up together — but the budget is the key's own: other services behind the same NAT address, or other traffic from your egress IP, do not draw it down. Other /api paths outside /api/v1 carry their own limits and are not part of this budget.

Requests to /api/v1 without an API key fall back to a per-client-IP limit of 60 per 60 seconds. A per-address flood ceiling of 1200 requests per 60 seconds also applies to keyed traffic; it exists to stop unauthenticated floods, sits at the combined budget of four fully-used keys, and a normal integration never encounters it.

Every response carries the current state:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 298
X-RateLimit-Reset: 1714000060000

X-RateLimit-Reset is epoch milliseconds. Over the limit you get 429 with Retry-After in seconds and this body:

{
  "error": "Too many requests. Please slow down.",
  "retryAfter": 14,
  "limit": 300,
  "remaining": 0,
  "reset": 1714000060000
}

In practice the integration shape stays far under this: one provisioning call per creator, one log call per upload, and webhooks for everything else. Backfills and nightly sweeps are where you need to pace yourself, so honor Retry-After and back off with jitter:

async function withRetry(fn, maxAttempts = 4) {
  for (let i = 0; i < maxAttempts; i++) {
    try { return await fn(); }
    catch (err) {
      if (err.status !== 429 || i === maxAttempts - 1) throw err;
      const wait = err.retryAfter
        ? err.retryAfter * 1000
        : Math.min(1000 * 2 ** i + Math.random() * 100, 10000);
      await new Promise(r => setTimeout(r, wait));
    }
  }
}

Identifiers

Every id Easy2257 returns is an opaque 25-character cuid with no prefix, for example cmd4k2x9p0001qz8h3v7ftg2a. Store them as strings, do not parse them, and do not assume a prefix or a fixed length beyond sizing your column generously.

The exceptions are the values Easy2257 formats for you: webhook event ids (evt_...), delivery ids (del_...), webhook signing secrets (whsec_...), and API keys (ez_live_...).

Webhooks

Deliveries go only to endpoints you register in the partner portal at partner.easy2257.com/webhooks. The callbackUrl field on a solo account, collab scene, or upload session is stored for your own reference and subscribes you to nothing.

See Webhooks for the envelope, the signature scheme, the full event list, and the retry schedule.

Versioning

The version is in the path. There is no version header, and no request is versioned by one.

Additive changes ship without notice: new optional fields, new endpoints, new event types, new error codes. Write your handlers to ignore fields they do not recognize and to treat an unknown code as its HTTP status. Anything breaking ships as a new path version, and the changelog records what moved.

On this page