Distribution

Manage buyer portals, buyers and deals, record title licenses, and check avails and platform submissions.

Distribution is the sell side of your catalog. A portal is a branded catalog you expose to buyers. A buyer is a company you sell to. A deal moves through stages from a buyer's request to delivery. A license is a record on a title of who holds what, where and for how long.

Rules the server enforces

  • Rights are never inferred. A license is a record. It is not an availability authority and never changes a title's rights status; rights windows on the title do that. See Rights.
  • Exclusive licenses can't silently overlap. A conflicting exclusive license is refused with 409 CONFLICT unless you override it with a reason, which is audited.
  • Viewers can't change licenses. A viewer key gets 403 FORBIDDEN when saving one.
  • A portal only shows what you've curated. Titles with expired rights can be hidden from it automatically.

Portals

curl https://dev.app.ingenee.ai/api/v1/portals \
  -H "Authorization: Bearer $INGENEE_API_KEY"

GET /v1/portals returns { portals: [...] }; GET /v1/portals/{id} returns { portal }:

200 OK (trimmed)
{
  "portal": {
    "id": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
    "slug": "ingenee-drama-avails",
    "name": "Ingenee drama avails",
    "status": "draft",
    "accessMode": "password",
    "hasPassword": true,
    "contentKind": "titles",
    "catalogIds": ["0e578f59-bf28-455f-bc82-f0639a7575fb"],
    "assetIds": [],
    "hiddenAssetIds": [],
    "channelIds": [],
    "hideExpiredRights": true,
    "buyerId": null,
    "accentColor": "#2f6fed",
    "about": "Scripted drama available for AVOD and FAST.",
    "contactEmail": "licensing@example.com"
  }
}

PATCH /v1/portals/{id} changes any of: name, slug, logoUrl, coverUrl, accentColor, logoBgColor, about, contactEmail, contactPhone, websiteUrl, catalogIds, assetIds (explicit titles), hiddenAssetIds, channelIds, hiddenChannelIds, hideExpiredRights, buyerId, status (draft or published), accessMode, password and restrictedDomains.

curl -X PATCH https://dev.app.ingenee.ai/api/v1/portals/2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f \
  -H "Authorization: Bearer $INGENEE_API_KEY" -H "content-type: application/json" \
  -d '{ "catalogIds": ["0e578f59-bf28-455f-bc82-f0639a7575fb"], "accessMode": "company_restricted", "restrictedDomains": ["buyer.example.com"], "status": "published" }'
  • Slug. An explicit slug is used exactly or refused (409 CONFLICT if taken, 422 if it isn't lower-case letters, numbers and hyphens). It is never silently suffixed.
  • Access. accessMode is public, private, password, invite, company_restricted, buyer_specific, team_members or sales_reps. company_restricted needs at least one bare domain in restrictedDomains, and buyer_specific needs accessBoundBuyerId, or the call returns 422 rather than leave the portal open. The password is stored hashed and never returned; hasPassword says whether one is set.
  • Images. logoUrl and coverUrl take an http(s) URL or an image data URL.
  • Curation. catalogIds, assetIds and hiddenAssetIds decide what buyers see. An empty catalogIds and assetIds means everything not hidden.

Licensing requests from buyers arrive on GET /v1/portals/{id}/requests: { requests, newCount }, each with the buyer's name, company, email, message, requested territories, license types and window, status and the titleName. A request with convertedDealId has already become a deal.

Buyers

curl -X POST https://dev.app.ingenee.ai/api/v1/distribution/buyers \
  -H "Authorization: Bearer $INGENEE_API_KEY" -H "content-type: application/json" \
  -d '{ "name": "Northlight Media", "type": "fast_platform", "website": "https://northlight.example.com",
        "territories": ["US", "CA"], "genreFocus": ["Drama", "Crime"], "businessModels": ["FAST"] }'

201 with { "buyer": { … } }. Only name is required. type is svod, avod, tvod, fast_platform, fast_channel, broadcaster, cable or other (default). territories are upper-cased. A buyer you add is private to your organization.

GET /v1/distribution/buyers returns { buyers, total, facets }. Filters: q, type, territory, genre, businessModel, status (active, inactive, unknown), limit (1 to 200, default 50) and offset. GET /v1/distribution/buyers/{id} returns the buyer with its evidence signals, contacts and your organization's own notes. The list can include buyers Ingenee maintains for all organizations; yours have isGlobal: false.

Deals

curl "https://dev.app.ingenee.ai/api/v1/distribution/deals?stage=approved" \
  -H "Authorization: Bearer $INGENEE_API_KEY"

GET /v1/distribution/deals returns { deals, counts, convertibleRequests, recorded }. recorded.value is null, not 0, when no won deal carries a price. GET /v1/distribution/deals/{id} returns { deal, titles, events, requestEvents, rights, emails, screeners, marketing }.

A deal's stage is new, approved, titles_sent, won, delivered or declined. Move it with a stage and an optional note:

curl -X POST https://dev.app.ingenee.ai/api/v1/distribution/deals/7d8e9f0a-1b2c-4d3e-8f4a-5b6c7d8e9f01/stage \
  -H "Authorization: Bearer $INGENEE_API_KEY" -H "content-type: application/json" \
  -d '{ "stage": "won", "note": "Signed 2026-10-28" }'
200 OK
{ "stage": "won", "from": "titles_sent" }

The move is appended to the deal's history; going back a stage is recorded too, never overwritten. Moving to the stage a deal is already in returns { "stage": "won", "unchanged": true }. Reaching won records the time.

Licenses on a title

curl -X POST https://dev.app.ingenee.ai/api/v1/titles/7c1a3f0e-5d2b-4f6a-9a41-2f0e9d3b8c11/licenses \
  -H "Authorization: Bearer $INGENEE_API_KEY" -H "content-type: application/json" \
  -d '{
    "licensee": "Northlight Media",
    "distributionType": "fast",
    "startDate": "2026-12-01",
    "endDate": "2027-11-30",
    "worldwide": false,
    "territories": ["US", "CA"],
    "exclusive": true,
    "dealType": "flat_fee",
    "currency": "USD",
    "licenseFee": 25000
  }'
201 Created (trimmed)
{ "license": { "id": "…", "licensee": "Northlight Media", "distributionType": "fast", "status": "upcoming", "exclusive": true, "territories": ["US", "CA"] }, "warnings": [] }
FieldNotes
licenseeRequired, up to 200 characters
distributionTypeRequired: avod, svod, tvod, fast, linear, broadcast, theatrical or other
startDate, endDateYYYY-MM-DD, or null for open. The end can't be before the start
worldwide, territoriesEither worldwide: true with no list, or false with at least one two-letter code
exclusiveExclusive licenses are checked for overlap
dealTypeowned, flat_fee, revenue_share or mg_revenue_share, with licenseFee, mgAmount, revenueShareLicensor, revenueShareLicensee (0 to 100), settlementFrequency (monthly, quarterly, yearly), currency
dealIdLink to one of the deals that applies to this title or its catalog
notesUp to 4,000 characters

status is computed on the server from the dates: current, upcoming or expired.

If the new exclusive license overlaps another on this title in time and territory, the save is refused:

409 Conflict (trimmed)
{ "error": { "code": "CONFLICT", "message": "Blocked: overlaps … Override with a reason to save anyway.",
             "details": { "code": "LICENSE_EXCLUSIVE_OVERLAP", "conflicts": [ ] } } }

To save anyway, resend with "override": true and an "overrideReason". An override without a reason returns 422 (details.code of LICENSE_OVERRIDE_REASON_REQUIRED). The reason and who overrode are stored on the license.

GET /v1/titles/{id}/licenses returns { licenses, dealLabels, warnings, today }. PATCH /v1/titles/{id}/licenses/{licenseId} takes the same fields as create and returns { license, warnings }. DELETE /v1/titles/{id}/licenses/{licenseId} returns { "deleted": true }.

Check an avails sheet

POST /v1/rights/avails/preview reads avails rows and says how they would match your Library. It writes nothing.

curl -X POST https://dev.app.ingenee.ai/api/v1/rights/avails/preview \
  -H "Authorization: Bearer $INGENEE_API_KEY" -H "content-type: application/json" \
  -d '{ "rows": [ { "title": "The Long Night", "year": 2019,
        "rights": [ { "scope": "AVOD", "territory": "US", "licenseStart": "2026-01-01", "licenseEnd": "2027-12-31" } ] } ] }'
200 OK (trimmed)
{
  "matched": [ { "title": "The Long Night", "assetId": "1b478b6a-…", "assetTitle": "The Long Night", "match": 1,
                 "windows": [ { "scope": "AVOD", "territory": "US", "start": "2026-01-01", "end": "2027-12-31" } ] } ],
  "needsReview": [ ],
  "fieldIssues": [ ],
  "counts": { "matched": 1, "needsReview": 0, "fieldIssues": 0 }
}

matched rows found a title with the same name (within a year). needsReview rows have only a fuzzy candidate in best, which a person must confirm. fieldIssues have no usable rights window. Up to 5,000 rows per call. Scope names are mapped to Ingenee's scopes (Theatrical, SVOD, TV, AVOD, FAST, TVOD). To record rights, add windows to titles with POST /v1/titles/{id}/rights, which is always an explicit decision.

Platform submissions

GET /v1/distribution/submissions returns the directory of distribution platforms, the state of each submission you've tracked (per platform and channel), your channels that can be offered, your own requests for platforms not in the directory, and counts (platforms, notStarted, considering, preparing, submitted, accepted, declined, openRequests). It is read-only.

Not yet available

  • Creating or deleting a portal, managing portal invitations, and replying to licensing requests.
  • Changing a request's status, or converting a request into a deal.
  • Updating submission states and applying an avails sheet in bulk.
  • Webhook events for deals or requests. See the available events.

Endpoints

OperationMethod + pathScope
List buyer portalsGET /v1/portalsdistribution:read
Get a portalGET /v1/portals/{id}distribution:read
Update a portalPATCH /v1/portals/{id}distribution:write
List licensing requestsGET /v1/portals/{id}/requestsdistribution:read
List buyersGET /v1/distribution/buyersdistribution:read
Add a buyerPOST /v1/distribution/buyersdistribution:write
Get a buyerGET /v1/distribution/buyers/{id}distribution:read
List dealsGET /v1/distribution/dealsdistribution:read
Get a dealGET /v1/distribution/deals/{id}distribution:read
Move a deal to a stagePOST /v1/distribution/deals/{id}/stagedistribution:write
List a title's licensesGET /v1/titles/{id}/licensesdistribution:read
Record a licensePOST /v1/titles/{id}/licensesdistribution:write
Update a licensePATCH /v1/titles/{id}/licenses/{licenseId}distribution:write
Delete a licenseDELETE /v1/titles/{id}/licenses/{licenseId}distribution:write
Partner submissions and statusGET /v1/distribution/submissionsdistribution:read
Preview an avails sheetPOST /v1/rights/avails/previewdistribution:read

On this page