//SCHEMADocs
Console

Releases

A release is one product: an album, a single, an EP, a video. It carries its identifiers (UPC, GRid), its titles in every language it was delivered in, its credits, its labels, and the tracks and artwork that make it up.

Releases reach your catalog two ways: delivered as DDEX ERN messages (over SFTP or through the API), or authored through the API as a draft and then published. Both produce the same object.

Only published, deliverable releases appear in releases and release. Drafts and blocked deliveries have their own queries — see Release lifecycle.

The Release object#

One product — an album, single, EP or video — as it exists in your catalog. Scalar fields are cheap; the fields that take arguments load the release's contents, batched across every release in the request.

Attributes

  • releasePkInt!
    Unique identifier within //SCHEMA.
  • upcStringnullable
    Universal Product Code, exactly as delivered. May be null on a draft.
  • gridStringnullable
    Global Release Identifier, when one was supplied.
  • titleStringnullable
    The default title. For every language and script, use titles.
  • stateString!
    draft, pending, active or withdrawn. See Release lifecycle.
  • availableFromDateTimenullable
    When the release became (or becomes) available in your catalog. Your catalog's own gate — distinct from the label's per-deal windows in deals.
  • updatedAtDateTime!
    When the release last changed.
  • locallyModifiedAtDateTimenullable
    When the release was last edited through the API after being delivered. null if it still matches what the label sent. A redelivery overwrites local edits.
  • withdrawnAtDateTimenullable
    When the release was withdrawn from your catalog — by a DDEX purge or by withdrawRelease. null unless state is withdrawn; reinstating clears it.
  • resources[Resource!]!
    Every resource on the release — tracks, artwork and anything else — flattened in disc/side order. Use groups when that structure matters.
    Arguments
    • Only resources of this kind — AUDIO for the tracks, IMAGE for the artwork. Measured once a file is analyzed, read from the resource type until then.
  • contributors[Credit!]!
    Parties credited on the release as a whole — display artists, the label, album-level credits. Track-level credits are on Resource.contributors.
    Arguments
    • roleString
      Only credits in this DDEX role, e.g. MainArtist or Producer. Case-insensitive.
    • territoryString
      Only credits that apply in this territory, e.g. US: credits scoped to it, plus unqualified credits where nothing more specific exists. An explicit Worldwide credit matches only territory: "Worldwide".
  • titles[LocalizedText!]!
    Every title of the release, in every language, script and territory it was delivered with.
    Arguments
    • typeString
      Only variants of this text type, e.g. DisplayTitle or FormalTitle. Case-insensitive.
    • languageString
      Only variants whose language tag is exactly this, e.g. ja-Jpan or ja-Latn. The whole tag, not a prefix: ja does not match ja-Jpan. Use script to select by script alone.
    • scriptString
      Only variants in this ISO 15924 script, e.g. Jpan or Latn.
    • territoryString
      Only variants that apply in this territory, e.g. US: variants scoped to it, plus unqualified variants where nothing more specific exists. See Territories and languages.
    • isDefaultBoolean
      Only variants whose default flag matches. Many senders flag none as default, so true can legitimately return an empty list.
  • plines[Pline!]!
    ℗ notices for the release as a whole.
    Arguments
    • languageString
      Only notices whose language tag is exactly this, e.g. en or ja-Jpan.
    • scriptString
      Only notices in this ISO 15924 script, e.g. Latn.
    • territoryString
      Only notices that apply in this territory: those scoped to it, plus unqualified notices where nothing more specific exists.
    • isDefaultBoolean
      Only notices whose default flag matches.
  • clines[Cline!]!
    © notices — the composition or artwork right, not the recording.
    Arguments
    • languageString
      Only notices whose language tag is exactly this, e.g. en or ja-Jpan.
    • scriptString
      Only notices in this ISO 15924 script, e.g. Latn.
    • territoryString
      Only notices that apply in this territory: those scoped to it, plus unqualified notices where nothing more specific exists.
    • isDefaultBoolean
      Only notices whose default flag matches.
  • labels[Label!]!
    The label(s) the release came out on, default first. Several is normal — an original label and a licensor, or a different label per territory.
  • groups[ResourceGroup!]!
    The disc/side structure, as a tree. A two-disc album has two top-level groups.
  • coverArtArtworknullablemedia_read

    The release's front cover at one of your organization's artwork sizes — the same answer as Resource.artwork on its FrontCoverImage, without finding the resource first: coverArt(size: "tile-600") { url width height }.

    null when the release has no front cover, or its file has not arrived. An unknown label is an UNKNOWN_ARTWORK_SIZE error.

    Arguments
  • dates[ReleaseDate!]!
    Dates the delivery asserted, such as ReleaseDate and OriginalReleaseDate.
  • deals[Deal!]!
    The availability windows the label asserted per commercial model and use type.
The Release object
{
  "releasePk": 48213,
  "upc": "0196871402293",
  "grid": null,
  "title": "Paper Radio",
  "state": "active",
  "availableFrom": "2025-03-14T00:00:00+00:00",
  "updatedAt": "2026-09-17T09:14:04.771520+00:00",
  "locallyModifiedAt": null,
  "withdrawnAt": null
}

The ReleaseState enum#

A release's availability state in your catalog, as a filter for releases. The state field on a release is the same value in lowercase.

Values

  • DRAFT
    Being authored; not yet published. Listing drafts needs catalog_write.
  • PENDING
    Delivered, waiting for its files.
  • ACTIVE
    Live.
  • WITHDRAWN
    Taken down, by a DDEX purge or withdrawRelease.

The ResourceGroup object#

One node of a release's disc/side tree. Groups nest, so a box set can hold discs that hold sides.

Attributes

  • groupPkInt!
    Unique identifier.
  • sequenceInt!
    Position among its siblings, from 1.
  • titleStringnullable
    The group's title, e.g. Disc 2, when one was given.
  • resources[Resource!]!
    The resources directly in this group, in order.
  • children[ResourceGroup!]!
    Nested groups.

The ReleaseDate object#

A release-level date from the delivery.

Attributes

  • dateTypeString!
    ReleaseDate, OriginalReleaseDate, or another type the sender used.
  • territoryStringnullable
    The territory the date applies to, or null if unqualified.
  • valueNaiveDate!
    The date.
The ReleaseDate object
{ "dateType": "OriginalReleaseDate", "territory": null, "value": "1994-11-02" }

The Deal object#

One availability window the label asserted: when a commercial model and use type may sell this release. A release can carry several.

Attributes

  • commercialModelStringnullable
    The DDEX commercial model, e.g. SubscriptionModel or AdvertisementSupportedModel.
  • useTypeStringnullable
    The DDEX use type, e.g. Stream or PermanentDownload.
  • startDateTimenullable
    Window start, or null if open-ended.
  • endDateTimenullable
    Window end, or null if open-ended.
The Deal object
{
  "commercialModel": "SubscriptionModel",
  "useType": "Stream",
  "start": "2025-03-14T00:00:00+00:00",
  "end": null
}

The PublishResult object#

The outcome of publishRelease. Validation problems come back here as data, not as GraphQL errors.

Attributes

  • publishedBoolean!
    true if the release is now in the catalog — including if it already was.
  • problems[String!]!
    Everything preventing publication, as sentences. Empty when published is true.
  • releaseReleasenullable
    The release as it now stands.
The PublishResult object
{
  "published": false,
  "problems": ["a UPC or GRid is required to publish", "no cover art"],
  "release": { "releasePk": 48290, "state": "draft" }
}

The CreateReleaseInput input object#

Release fields you can set when creating or updating a release. Blank strings are treated as absent.

Fields

  • Universal Product Code. Optional while drafting; a UPC or GRid is required to publish.
  • gridString
    Global Release Identifier.
  • titleString
    The release title.
  • releaseTypeString
    The DDEX release type, e.g. Album, Single, EP. Values outside the DDEX set are accepted and flagged.
  • The release's ℗ notices — who owns the recordings. Omit to keep what is there; a list replaces them all; [] clears. Not required to publish. See CopyrightLineInput.
  • The release's © notices — who owns the compositions and artwork. Same rule as plines.
  • labels[LabelInput!]
    The label(s) the release came out on, as parties. Same rule as plines. See LabelInput.

List releases#

queryreleases→[Release!]!catalog_read

Returns releases in your catalog, most recently updated first, optionally narrowed by a search string.

By default only active and withdrawn releases are listed — the published catalog. Pass states to choose: [WITHDRAWN] for takedowns, [PENDING] for deliveries still waiting for files, or [DRAFT] for unpublished work, which needs the catalog_write scope, as draftReleases does. To find out why a pending release is waiting, see blockedReleases.

Page with limit and offset, and use releasesCount with the same q and states to know when to stop. See Paginate and search.

Arguments

  • limitInt!Default: 50
    Maximum number of releases to return. Clamped to 1–200.
  • offsetInt!Default: 0
    Number of releases to skip. Negative values are treated as 0.
  • Case- and accent-insensitive partial match against the release title, a release-level display artist, or a label name. A release matches if any of the three matches. Accents fold on both sides, so sinead finds Sinéad.
  • states[ReleaseState!]
    Only releases in these states. Omitted, ACTIVE and WITHDRAWN. An empty list matches nothing. Including DRAFT needs catalog_write.

Returns

A list of Release objects. Empty when nothing matches.

Request
query ListReleases($q: String) {
  releases(q: $q, limit: 2) {
    releasePk
    upc
    title
    state
    availableFrom
  }
}
Variables
{
  "q": "Halfway Ghosts"
}
Response
{
  "data": {
    "releases": [
      {
        "releasePk": 48213,
        "upc": "0196871402293",
        "title": "Paper Radio",
        "state": "active",
        "availableFrom": "2025-03-14T00:00:00+00:00"
      },
      {
        "releasePk": 47102,
        "upc": "0196871118736",
        "title": "Slow Exit",
        "state": "active",
        "availableFrom": "2023-09-01T00:00:00+00:00"
      }
    ]
  }
}

Count releases#

queryreleasesCount→Int!catalog_read

The total number of releases releases can page through for the same q and states. A page that comes back full is not proof there is another; this is.

Arguments

  • The same search string you pass to releases.
  • states[ReleaseState!]
    The same states you pass to releases. Including DRAFT needs catalog_write.

Returns

An Int.

Request
query {
  releasesCount(q: "Halfway Ghosts")
}
Response
{
  "data": {
    "releasesCount": 7
  }
}

Retrieve a release#

queryrelease→Releasecatalog_read

Fetch one release by UPC, with as much of its tree as you select — tracks, credits, titles, artwork URLs.

Returns null both when no release has that UPC and when one exists but not in your catalog; the two are deliberately indistinguishable. Drafts are not returned — use draftRelease.

Arguments

  • upcString!required
    The release's UPC, exactly as delivered (leading zeros included).

Returns

A Release, or null.

Request
query GetRelease($upc: String!) {
  release(upc: $upc) {
    title
    contributors(role: "MainArtist") {
      party { name }
    }
    resources {
      sequence
      isrc
      title
      durationSeconds
      state
    }
  }
}
Variables
{
  "upc": "0196871402293"
}
Response
{
  "data": {
    "release": {
      "title": "Paper Radio",
      "contributors": [
        { "party": { "name": "Halfway Ghosts" } }
      ],
      "resources": [
        {
          "sequence": 1,
          "isrc": "JPTO09404900",
          "title": "Signal Fire",
          "durationSeconds": 214,
          "state": "stored"
        },
        {
          "sequence": 2,
          "isrc": "JPTO09404910",
          "title": "Paper Radio",
          "durationSeconds": 187,
          "state": "stored"
        },
        {
          "sequence": 3,
          "isrc": null,
          "title": null,
          "durationSeconds": null,
          "state": "stored"
        }
      ]
    }
  }
}

Retrieve a draft#

querydraftRelease→Releasecatalog_write

Fetch one release by releasePk, including drafts. This is how an authoring client reads back its own unpublished work.

Requires catalog_write rather than catalog_read: unpublished work is visible only to credentials that can author.

Arguments

Returns

A Release in any state, or null if it is not in your catalog.

Request
query {
  draftRelease(releasePk: 48290) {
    releasePk
    title
    state
    upc
  }
}
Response
{
  "data": {
    "draftRelease": {
      "releasePk": 48290,
      "title": "Night Ferry",
      "state": "draft",
      "upc": null
    }
  }
}

List drafts#

querydraftReleases→[Release!]!catalog_write

Releases still being authored, most recently updated first.

Arguments

  • limitInt!Default: 25
    Maximum number to return. Clamped to 1–200.

Returns

A list of Release objects in state draft.

Request
query {
  draftReleases(limit: 10) {
    releasePk
    title
    updatedAt
  }
}
Response
{
  "data": {
    "draftReleases": [
      {
        "releasePk": 48290,
        "title": "Night Ferry",
        "updatedAt": "2026-09-17T16:02:11.482913+00:00"
      }
    ]
  }
}

Create a draft release#

mutationcreateRelease→Release!catalog_write

Start a release. It is created in state draft and is private to your catalog until you publish it — it does not appear in releases or release(upc:).

Every input field is optional while drafting. A UPC or GRid is required to publish, unless your organization is exempt.

One call can carry the whole release's metadata: the ℗ and © notices (plines, clines) and the label (labels, a party from createParty) go in the same input. None of them is required to publish.

Arguments

  • inputCreateReleaseInput!required
    What you know about the release so far.
    Show child parameters
    • Universal Product Code. Optional while drafting; a UPC or GRid is required to publish.
    • gridString
      Global Release Identifier.
    • titleString
      The release title.
    • releaseTypeString
      The DDEX release type, e.g. Album, Single, EP. Values outside the DDEX set are accepted and flagged.
    • The release's ℗ notices — who owns the recordings. Omit to keep what is there; a list replaces them all; [] clears. Not required to publish. See CopyrightLineInput.
    • The release's © notices — who owns the compositions and artwork. Same rule as plines.
    • labels[LabelInput!]
      The label(s) the release came out on, as parties. Same rule as plines. See LabelInput.

Returns

The new Release. Keep its releasePk.

Request
mutation CreateRelease($input: CreateReleaseInput!) {
  createRelease(input: $input) {
    releasePk
    state
    title
    plines { value }
    labels { partyPk name }
  }
}
Variables
{
  "input": {
    "title": "Night Ferry",
    "releaseType": "Album",
    "plines": [
      {
        "text": "(P) 2026 Lanternfish Records",
        "year": "2026"
      }
    ],
    "labels": [
      {
        "partyPk": 90211
      }
    ]
  }
}
Response
{
  "data": {
    "createRelease": {
      "releasePk": 48290,
      "state": "draft",
      "title": "Night Ferry",
      "plines": [
        {
          "value": "(P) 2026 Lanternfish Records"
        }
      ],
      "labels": [
        {
          "partyPk": 90211,
          "name": "Lanternfish Records"
        }
      ]
    }
  }
}

Update a release#

mutationupdateRelease→Release!catalog_write

Change a release's UPC, GRid, title, type, ℗ and © notices or label. Only the fields you pass are changed; omitted fields keep their current value. Blank strings are treated as omitted.

The lists — plines, clines, labels — replace: passing one sets exactly those entries, and [] removes them all. That is the only way to clear something here.

Works on published and delivered releases too, so a typo in a delivered release can be fixed without a redelivery. Editing a delivered release sets locallyModifiedAt — and a later redelivery from the label will overwrite your correction.

Arguments

  • releasePkInt!required
    The release to change.
  • inputCreateReleaseInput!required
    The fields to change.
    Show child parameters
    • Universal Product Code. Optional while drafting; a UPC or GRid is required to publish.
    • gridString
      Global Release Identifier.
    • titleString
      The release title.
    • releaseTypeString
      The DDEX release type, e.g. Album, Single, EP. Values outside the DDEX set are accepted and flagged.
    • The release's ℗ notices — who owns the recordings. Omit to keep what is there; a list replaces them all; [] clears. Not required to publish. See CopyrightLineInput.
    • The release's © notices — who owns the compositions and artwork. Same rule as plines.
    • labels[LabelInput!]
      The label(s) the release came out on, as parties. Same rule as plines. See LabelInput.

Returns

The updated Release. Errors with no such release in this catalog if it is not yours.

Request
mutation {
  updateRelease(releasePk: 48290, input: { upc: "0196871555012" }) {
    releasePk
    upc
    locallyModifiedAt
  }
}
Response
{
  "data": {
    "updateRelease": {
      "releasePk": 48290,
      "upc": "0196871555012",
      "locallyModifiedAt": null
    }
  }
}

Publish a release#

mutationpublishRelease→PublishResult!catalog_write

Put a draft into the catalog. On success the release becomes active, gets an availableFrom, and appears in releases.

A release can be published when it has:

  • a UPC or a GRid,
  • at least one resource,
  • uploaded bytes for every resource, and
  • a front cover image (FrontCoverImage) with uploaded bytes.

If anything is missing, nothing changes and every problem comes back in problems — as data, not as a GraphQL error — so you can show a checklist and fix everything in one pass.

Idempotent: publishing an already-published release returns published: true with no problems.

Arguments

  • releasePkInt!required
    The draft to publish.

Returns

A PublishResult.

Request
mutation {
  publishRelease(releasePk: 48290) {
    published
    problems
    release { state availableFrom }
  }
}
Response
{
  "data": {
    "publishRelease": {
      "published": false,
      "problems": [
        "1 resource(s) have no uploaded bytes; complete their uploads before publishing"
      ],
      "release": { "state": "draft", "availableFrom": null }
    }
  }
}

Withdraw a release#

mutationwithdrawRelease→Release!catalog_write

Take a release out of your catalog — a takedown. The release becomes withdrawn, gets a withdrawnAt, and a release.state_changed webhook announces it with to: "withdrawn".

This is the manual counterpart of a DDEX PurgeReleaseMessage, for a takedown that reached you some other way: an email from the label, a rights dispute, a mistake. A purge delivered over SFTP or through deliverMessage does the same thing on its own.

Withdrawal is a state, not a delete. The release, its tracks and its files stay, it is still listed by releases with state: "withdrawn", and reinstateRelease undoes it. Media URLs are not refused for a withdrawn release; what you do with its files is up to your agreement with the label.

Works on active releases and on pending ones still waiting for files — a withdrawn release does not go live when its files arrive. Withdrawing a release that is already withdrawn returns it unchanged. A draft cannot be withdrawn: it was never published.

Arguments

  • releasePkInt!required
    The release to withdraw.

Returns

The Release as it now stands. Errors with no release … in this catalog if it is not yours, and when the release is still a draft.

Request
mutation {
  withdrawRelease(releasePk: 48290) {
    releasePk
    state
    withdrawnAt
  }
}
Response
{
  "data": {
    "withdrawRelease": {
      "releasePk": 48290,
      "state": "withdrawn",
      "withdrawnAt": "2026-10-31T14:02:11.504193+00:00"
    }
  }
}

Reinstate a release#

mutationreinstateRelease→Release!catalog_write

Put a withdrawn release back. It returns to the state it was withdrawn from: active if it was live; pending if it was still waiting for files, going live when they arrive — or at once, if they arrived while it was withdrawn.

This is the only way back. Redelivering a withdrawn release updates its metadata but leaves it withdrawn, so a label resending an old message cannot undo a takedown.

A release that is not withdrawn is returned unchanged.

Arguments

  • releasePkInt!required
    The release to reinstate.

Returns

The Release as it now stands. Errors with no release … in this catalog if it is not yours.

Request
mutation {
  reinstateRelease(releasePk: 48290) {
    releasePk
    state
    withdrawnAt
  }
}
Response
{
  "data": {
    "reinstateRelease": {
      "releasePk": 48290,
      "state": "active",
      "withdrawnAt": null
    }
  }
}