Resources
A resource is one file-backed asset in a release: a sound recording, a cover image, a video. DDEX calls them resources, and so does this API.
A resource can exist before its bytes do — a delivered message can
arrive before its audio — so every resource has a state: incoming
until its file lands, then stored. See
Out-of-order delivery.
To put bytes behind a resource, see Uploads.
- mutationaddResource
- mutationupdateResource
- mutationreorderTracks
- mutationremoveResource
The Resource object#
A track, image or video on a release.
Attributes
resourcePkInt!Unique identifier.- International Standard Recording Code, for sound recordings.
- The DDEX resource type, e.g.
MusicalWorkSoundRecordingorFrontCoverImage. - The default title. For every variant, use
titles. - Duration in whole seconds.
stateString!- File size in bytes, once the file has arrived.
- An opaque value that changes whenever this resource's file is replaced. Use it as a cache version for anything made from the file, such as a resized cover. It is not a content address and cannot be used to fetch anything.
nulluntil the file has arrived. sequenceInt!Position within its group, from 1.- What //SCHEMA measured from the file.
nulluntil it has been analyzed — usually seconds after it lands; resource.analyzed says when. mediaKindMediaKind!AUDIO,IMAGE,VIDEOorOTHER. Measured once the file is analyzed; read fromresourceTypeuntil then.contributors[Credit!]!Parties credited on this recording — performers, producers, engineers, writers.Arguments
roleStringOnly credits in this DDEX role, e.g.MainArtistorProducer. Case-insensitive.territoryStringOnly credits that apply in this territory, e.g.US: credits scoped to it, plus unqualified credits where nothing more specific exists. An explicitWorldwidecredit matches onlyterritory: "Worldwide".
titles[LocalizedText!]!Every title of this resource, in every language, script and territory.Arguments
typeStringOnly variants of this text type, e.g.DisplayTitleorFormalTitle. Case-insensitive.languageStringOnly variants whose language tag is exactly this, e.g.ja-Jpanorja-Latn. The whole tag, not a prefix:jadoes not matchja-Jpan. Usescriptto select by script alone.scriptStringOnly variants in this ISO 15924 script, e.g.JpanorLatn.territoryStringOnly 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.isDefaultBooleanOnly variants whose default flag matches. Many senders flag none as default, sotruecan legitimately return an empty list.
plines[Pline!]!℗ notices for this recording. On a compilation these can differ from the release's.Arguments
languageStringOnly notices whose language tag is exactly this, e.g.enorja-Jpan.scriptStringOnly notices in this ISO 15924 script, e.g.Latn.territoryStringOnly notices that apply in this territory: those scoped to it, plus unqualified notices where nothing more specific exists.isDefaultBooleanOnly notices whose default flag matches.
clines[Cline!]!© notices for this resource — in DDEX, those of artwork, text and video. A sound recording's © is on the release.Arguments
languageStringOnly notices whose language tag is exactly this, e.g.enorja-Jpan.scriptStringOnly notices in this ISO 15924 script, e.g.Latn.territoryStringOnly notices that apply in this territory: those scoped to it, plus unqualified notices where nothing more specific exists.isDefaultBooleanOnly notices whose default flag matches.
encodings[Encoding!]!Every file of this resource: the original you delivered, and each encoding //SCHEMA has made of it, with what it measured about each. Lists, never mints — pick one and pass itsencoding(andclip) tourl. See Encodings and media URLs.A short-lived signed URL for one file of this resource — the only way to get one. You always name the file:
original(the bytes you delivered), an encoding family such asaac-256(its newest version), or a pinned encoding such asaac-256-v1.Which scope it needs depends on what the file is:
media_preview(ormedia_read) for clips and AAC 128;media_readfor other lossy encodings and for artwork;media_losslessand an organization with lossless media for anything lossless — FLAC, WAV, ALAC, or a lossless original. A file not yet analyzed counts as lossless.nullwhile the resource isincoming. Any othernullcomes with an error whoseextensions.codesays why — see Encodings and media URLs. Every URL minted is logged. The release does not have to be live.Arguments
- The 30-second preview clip of that encoding instead of full length. Lossy encodings only.
ttlSecondsIntHow long the URL stays valid, in seconds. Default 300; values above 3600 are capped at 3600; 0 is an error.- Have a browser save the file as
Artist - Title.extinstead of playing it. The name is signed into the URL.
This cover at one of your organization's artwork sizes, by label:
artwork(size: "tile-600") { url width height }.media_read, as for the original.PENDINGwith no URL until the rendition is made.nullwhile the resource isincoming. An unknown label is anUNKNOWN_ARTWORK_SIZEerror listing your labels; a resource that is not an image isENCODING_NOT_AVAILABLE, and an image that is not theFrontCoverImageisARTWORK_NOT_FRONT_COVER— sizes are made of front covers only. Release.coverArt finds the cover for you.Arguments
- A label from artworkSizes, e.g.
tile-600.
{
"resourcePk": 771201,
"isrc": "JPTO09404900",
"resourceType": "MusicalWorkSoundRecording",
"title": "Signal Fire",
"durationSeconds": 214,
"state": "stored",
"byteSize": 48213377,
"contentVersion": "3b9e1f0c7a42d851",
"sequence": 1,
"mediaKind": "AUDIO",
"technical": {
"mediaKind": "audio",
"container": "flac",
"codec": "flac",
"bitrateKbps": 1802,
"sampleRateHz": 44100,
"bitDepth": 16,
"channels": 2,
"durationMs": 214306,
"lossless": true,
"integratedLufs": -9.8,
"truePeakDbtp": -0.3,
"widthPx": null,
"heightPx": null
}
}The Technical object#
What //SCHEMA measured from the file itself, never what the sender asserted. Compare durationMs with the delivered durationSeconds to catch a truncated or wrong file.
Attributes
audio,image,video, orotherfor a file that is none of them.- The file format, e.g.
wav,flac,aiff,mp4,jpeg. - The codec, e.g.
pcm_s24le,flac,mp3,jpeg. - Bitrate in kilobits per second.
- Sample rate in hertz, e.g.
44100. - Bits per sample, e.g.
16or24. Lossless audio only. - Number of audio channels.
- Measured duration, in milliseconds.
- Whether the audio is lossless.
nullfor anything that is not audio. - EBU R128 integrated loudness, in LUFS — what DSPs normalize on.
- EBU R128 true peak, in dBTP.
- Image or video width in pixels.
- Image or video height in pixels.
{ "resourcePk": 771340, "isrc": "JPTO09404920", "state": "incoming" }The AddResourceInput input object#
A resource to add to a release.
Fields
- The release to add it to. Must be in your catalog.
isrcStringInternational Standard Recording Code.- The DDEX resource type —
MusicalWorkSoundRecordingfor a track,FrontCoverImagefor cover art. - The vocabulary
resourceTypebelongs to.SoundRecordingTypefor audio; useImageTypefor artwork. titleStringThe title.durationSecondsIntDuration in whole seconds.sequenceIntTrack number. Omit to append after the last track.
Add a resource#
Add a track or an image to a draft release. The resource starts as
incoming; upload its bytes with
requestUpload and
completeUpload.
Cover art is a resource like any other: add it with
resourceType: "FrontCoverImage" and resourceTypeKind: "ImageType".
Arguments
- The resource to add.
Show child parameters
- The release to add it to. Must be in your catalog.
isrcStringInternational Standard Recording Code.- The DDEX resource type —
MusicalWorkSoundRecordingfor a track,FrontCoverImagefor cover art. - The vocabulary
resourceTypebelongs to.SoundRecordingTypefor audio; useImageTypefor artwork. titleStringThe title.durationSecondsIntDuration in whole seconds.sequenceIntTrack number. Omit to append after the last track.
Returns
A DraftResource. Keep its resourcePk — you upload against it.
mutation AddTrack($input: AddResourceInput!) {
addResource(input: $input) {
resourcePk
state
}
}{
"input": {
"releasePk": 48290,
"resourceType": "MusicalWorkSoundRecording",
"title": "Harbour Lights",
"isrc": "JPTO09404920"
}
}curl https://api.schema.fm/graphql \
-H "Authorization: Bearer $SCHEMA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"query": "mutation AddTrack($input: AddResourceInput!) {\n addResource(input: $input) {\n resourcePk\n state\n }\n}",
"variables": {
"input": {
"releasePk": 48290,
"resourceType": "MusicalWorkSoundRecording",
"title": "Harbour Lights",
"isrc": "JPTO09404920"
}
}
}
EOFconst res = await fetch("https://api.schema.fm/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
mutation AddTrack($input: AddResourceInput!) {
addResource(input: $input) {
resourcePk
state
}
}
`,
variables: {
"input": {
"releasePk": 48290,
"resourceType": "MusicalWorkSoundRecording",
"title": "Harbour Lights",
"isrc": "JPTO09404920"
}
},
}),
});
const { data, errors } = await res.json();{
"data": {
"addResource": {
"resourcePk": 771340,
"state": "incoming"
}
}
}Update a resource#
Rename a track, correct its ISRC or duration, or set its own ℗ and © notices. Only the arguments you pass are changed. This is what lets a client upload files first and name them afterwards.
Arguments
- The release the resource belongs to.
- The resource to change.
titleStringNew title.isrcStringNew ISRC.durationSecondsIntNew duration, in whole seconds.plines[CopyrightLineInput!]This track's ℗ notices, when they differ from the release's — a licensed-in track on a compilation, say. A list replaces them all;[]clears.Show child parameters
- The notice as displayed, e.g.
(P) 2026 Lanternfish Records. Stored exactly as given. yearStringThe year, as a string.companyStringThe rights holder, when not only insidetext.languageAndScriptCodeStringA BCP-47 tag such asenorja-Jpan, when the notice is in one language. The script is extracted as it is from a delivery.territoryStringLeave unset unless the notice applies to one territory only. Unset is not the same asWorldwide.- The default among several. At most one per list; when none is marked, the first is.
clines[CopyrightLineInput!]This resource's © notices — usually for artwork, text or video. Same rule asplines.Show child parameters
- The notice as displayed, e.g.
(P) 2026 Lanternfish Records. Stored exactly as given. yearStringThe year, as a string.companyStringThe rights holder, when not only insidetext.languageAndScriptCodeStringA BCP-47 tag such asenorja-Jpan, when the notice is in one language. The script is extracted as it is from a delivery.territoryStringLeave unset unless the notice applies to one territory only. Unset is not the same asWorldwide.- The default among several. At most one per list; when none is marked, the first is.
Returns
true if the resource was updated, false if it is not in that release.
mutation {
updateResource(releasePk: 48290, resourcePk: 771340, title: "Harbour Lights (Reprise)")
}curl https://api.schema.fm/graphql \
-H "Authorization: Bearer $SCHEMA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"query": "mutation {\n updateResource(releasePk: 48290, resourcePk: 771340, title: \"Harbour Lights (Reprise)\")\n}"
}
EOFconst res = await fetch("https://api.schema.fm/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
mutation {
updateResource(releasePk: 48290, resourcePk: 771340, title: "Harbour Lights (Reprise)")
}
`,
}),
});
const { data, errors } = await res.json();{
"data": {
"updateResource": true
}
}Reorder tracks#
Set the track order. resourcePks is the new order, first to last.
Resources you leave out keep their relative order after the ones you
list, so a partial list reorders rather than truncates.
Track numbers (sequence) are recomputed as 1…n.
If any id is not in this release, nothing changes and the result is
false.
Arguments
- The release to reorder.
- Resource ids in their new order.
Returns
true when the new order was applied.
mutation {
reorderTracks(releasePk: 48290, resourcePks: [771341, 771340])
}curl https://api.schema.fm/graphql \
-H "Authorization: Bearer $SCHEMA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"query": "mutation {\n reorderTracks(releasePk: 48290, resourcePks: [771341, 771340])\n}"
}
EOFconst res = await fetch("https://api.schema.fm/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
mutation {
reorderTracks(releasePk: 48290, resourcePks: [771341, 771340])
}
`,
}),
});
const { data, errors } = await res.json();{
"data": {
"reorderTracks": true
}
}Remove a resource#
Take a track or image off a release and delete it. Idempotent:
removing one that is already gone returns false rather than an
error.
Uploaded bytes are not deleted from storage — media is stored by content address, and may back other resources.
Arguments
- The release to remove it from.
- The resource to remove.
Returns
true if a resource was removed.
mutation {
removeResource(releasePk: 48290, resourcePk: 771341)
}curl https://api.schema.fm/graphql \
-H "Authorization: Bearer $SCHEMA_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'EOF'
{
"query": "mutation {\n removeResource(releasePk: 48290, resourcePk: 771341)\n}"
}
EOFconst res = await fetch("https://api.schema.fm/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SCHEMA_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
mutation {
removeResource(releasePk: 48290, resourcePk: 771341)
}
`,
}),
});
const { data, errors } = await res.json();{
"data": {
"removeResource": true
}
}