> For the complete documentation index, see [llms.txt](https://knowledgebase.fabricdata.com/studio/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://knowledgebase.fabricdata.com/studio/integrations-and-apis/origin-studio-production-api/licensed-metadata.md).

# Licensed Metadata

Enable or disable a licensed third-party metadata provider (e.g. Gracenote) for a tenant, using the tenant's own API key. Unlike Global Metadata, this API performs real per-tenant mutations and provisions dedicated, isolated storage for that tenant's licensed data.

## List a tenant's licensed metadata provider configs

> Returns the caller's tenant's licensed metadata provider configs. Filter to one provider with \`filter.provider\`, or omit it to list every provider configured for the tenant. A provider with no config is not enabled.

```json
{"openapi":"3.0.3","info":{"title":"Fabric API - Audit","version":"0.9.2264"},"tags":[{"name":"Licensed Metadata","description":"Enable or disable a licensed third-party metadata provider (e.g. Gracenote) for a tenant, using the tenant's own API key. Unlike Global Metadata, this API performs real per-tenant mutations and provisions dedicated, isolated storage for that tenant's licensed data."}],"servers":[{"url":"{protocol}://{domain}/{basePath}","variables":{"protocol":{"enum":["http","https"],"default":"https"},"domain":{"default":"api.studio.fabricdata.com"},"basePath":{"default":"v1"}}}],"security":[{"jwt-apikey-authorizer":[]}],"components":{"securitySchemes":{"jwt-apikey-authorizer":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"LicensedMetadataConfigFindManyResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataConfigFindManyResponse","description":"The response for a licensed metadata configs find many request.","additionalProperties":false,"required":["data","links"],"type":"object","properties":{"data":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/BaseResource"},{"type":"object","properties":{"attributes":{"allOf":[{"$ref":"#/components/schemas/BaseResourceAttributes"},{"$ref":"#/components/schemas/LicensedMetadataConfigAttributes"}]},"meta":{"type":"object","description":"Non-attribute reference info. seedJobId isn't a JSON:API relationship because there's no dedicated backgroundJobs relationship endpoint here — check its status via GET /backgroundJobs/{jobId}.","properties":{"seedJobId":{"type":"string","description":"ID of the background job that ran (or is running) the initial seed, if one has been started."}},"additionalProperties":false}},"required":["attributes"],"additionalProperties":false}]}},"links":{"type":"object","required":["self"],"properties":{"self":{"type":"string"}},"additionalProperties":false}}},"BaseResource":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResource","description":"JSON:API resource identifier. All Origin Studio API resource objects include at least an `id` (unique string identifier) and a `type` (the JSON:API resource type name, e.g. records, contributors).","type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"}},"required":["id","type"],"additionalProperties":false},"BaseResourceAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResourceAttributes","description":"Common timestamps present on all resource attribute objects. `created` is the ISO 8601 date-time when the resource was first created; `updated` is the date-time of the most recent modification.","type":"object","additionalProperties":false,"required":["created","updated"],"properties":{"created":{"type":"string","format":"date-time"},"updated":{"type":"string","format":"date-time"}}},"LicensedMetadataConfigAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataConfigAttributes","description":"Per-tenant licensed metadata provider config (read-only view). Existence of the resource means the provider is enabled for this tenant; delete disables it. Shows which provider is configured and whether an API key is stored — the raw key is never returned, only a partial preview.","type":"object","additionalProperties":false,"required":["provider","apiKeyConfigured","programsPollingIntervalMinutes","taxonomyPollingInterval","seedingStatus"],"properties":{"provider":{"type":"string","enum":["gracenote"],"description":"The licensed metadata provider this config enables for the tenant."},"apiKeyConfigured":{"type":"boolean","description":"Whether an API key has been securely stored for this provider."},"apiKeyPreview":{"type":"string","description":"First 2 and last 2 characters of the stored API key (e.g. \"gc••••9a\"), for display only. Never enough to reconstruct the key. Absent until an API key has been stored at least once."},"programsPollingIntervalMinutes":{"type":"integer","enum":[15,30,45,60],"description":"How often program updates are checked for once caught up on the initial seed."},"taxonomyPollingInterval":{"type":"string","enum":["daily","weekly"],"description":"How often the provider's reference taxonomy is checked for changes."},"pollingPaused":{"type":"boolean","description":"True when the tenant has paused steady-state polling. While paused, both feeds' schedules are disabled and a steady-state drain already in flight stops at its next page, so no further provider API quota is spent on polling. An initial seed job already running is NOT stopped and will finish its crawl. Cursors, cached data and the configured cadence are all retained — resuming continues from where polling stopped rather than re-crawling. Absent means not paused."},"seedingStatus":{"type":"string","enum":["not_started","seeding","finished","failed"],"description":"Status of the initial data seed. Ongoing polling only starts once this reaches 'finished'."},"seedingError":{"type":"string","description":"Customer-safe reason the initial seed failed. Present only when seedingStatus is 'failed'."},"seedingStartedAt":{"type":"string","format":"date-time","description":"When the most recent initial seed was launched. Recorded on this resource rather than read back from the seed's background-job row, because those rows are deleted by a 7-day TTL while this timestamp has to stay visible for the life of the integration. Absent until a seed has been launched at least once."},"seedingFinishedAt":{"type":"string","format":"date-time","description":"When the most recent initial seed reached a terminal state, whether it succeeded or failed. Absent while a seed is running, and for seeds launched before this field existed."},"polling":{"type":"object","additionalProperties":false,"description":"Steady-state polling health per feed, so a tenant can tell whether ongoing ingestion is working after the initial seed finishes. A feed key is absent until that feed has been polled at least once.","properties":{"programs":{"$ref":"#/components/schemas/LicensedMetadataFeedHealth"},"taxonomy":{"$ref":"#/components/schemas/LicensedMetadataFeedHealth"}}},"sandbox":{"type":"boolean","description":"True when this config is using the Fabric sandbox API key, so its cached records are synthetic test data rather than licensed Gracenote content. Sandbox titles also carry a TMSId prefix Gracenote never issues (ZZ...), so they stay identifiable outside this response. Read-only — derived from the key, not settable."}}},"LicensedMetadataFeedHealth":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataFeedHealth","description":"Steady-state health of one licensed-metadata feed's polling. Present once the feed has been polled at least once.","type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","enum":["pending","ok","unauthorized","error"],"description":"Outcome of the most recent poll. 'pending' means the feed is scheduled but hasn't completed a poll yet; 'ok' succeeded; 'unauthorized' means the provider rejected the API key — rotate it via PATCH; 'error' is any other failure."},"lastPolledAt":{"type":"string","description":"ISO timestamp of the most recent poll attempt, whatever its outcome."},"lastSuccessAt":{"type":"string","description":"ISO timestamp of the most recent successful poll. A stale value here while status is not 'ok' means ingestion is currently broken."},"nextPollAt":{"type":"string","description":"Estimated ISO timestamp of the next poll — lastPolledAt plus this feed's configured interval. An estimate: the actual schedule fires on a fixed cadence, so a change to the interval only takes effect from the next tick."},"caughtUp":{"type":"boolean","description":"Whether the feed had no further pending updates as of the last successful poll."},"stalled":{"type":"boolean","description":"Present and true when the feed is overdue — the current time is well past nextPollAt, meaning the scheduler appears to have stopped firing rather than the feed being merely idle. A stalled feed's status/message reflect its last known outcome, which may look healthy; treat stalled as a distinct 'polling has silently stopped' signal. Absent when the feed is polling on schedule."},"behindBy":{"type":"integer","description":"Approximate number of update-stream positions still pending as of the last successful poll (0 when caught up)."},"message":{"type":"string","description":"Human-readable detail, present for non-ok statuses — what went wrong and what to do about it."}}},"ErrorResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IErrorResponse","description":"Standard JSON:API error response returned when a request fails. Contains an `errors` array with one or more error objects (status, title, and optional detail).","type":"object","required":["errors"],"additionalProperties":false,"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"}}}},"Error":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IError","description":"A single JSON:API error object. `status` is the HTTP status code as a string. `title` is a short, human-readable summary. `detail` provides optional context. `meta` may include structured conflict or authorization details.","type":"object","additionalProperties":false,"required":["status","title"],"properties":{"status":{"type":"string"},"title":{"type":"string"},"detail":{"type":"string"},"source":{"type":"object","description":"A JSON Pointer to the request field that caused the error (e.g. `/data/attributes/title`).","properties":{"pointer":{"type":"string"}}},"meta":{"type":"object","additionalProperties":true,"properties":{"stack":{"type":"string","description":"Error stack trace. Only present in non-production environments."},"conflictItems":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}},"unauthorizedItems":{"type":"array","items":{"type":"string"}}}}}}}},"paths":{"/global/licensedMetadataConfigs":{"get":{"summary":"List a tenant's licensed metadata provider configs","operationId":"licensedMetadataConfigFindMany","description":"Returns the caller's tenant's licensed metadata provider configs. Filter to one provider with `filter.provider`, or omit it to list every provider configured for the tenant. A provider with no config is not enabled.","tags":["Licensed Metadata"],"parameters":[{"name":"filter.provider","description":"Restrict the result to one provider's config.","in":"query","required":false,"schema":{"type":"string","enum":["gracenote"]}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LicensedMetadataConfigFindManyResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Enable a licensed metadata provider for a tenant

> Enable a licensed third-party metadata provider (e.g. Gracenote) for the caller's tenant, using the tenant's own API key. A tenant may only have one config per provider.

```json
{"openapi":"3.0.3","info":{"title":"Fabric API - Audit","version":"0.9.2264"},"tags":[{"name":"Licensed Metadata","description":"Enable or disable a licensed third-party metadata provider (e.g. Gracenote) for a tenant, using the tenant's own API key. Unlike Global Metadata, this API performs real per-tenant mutations and provisions dedicated, isolated storage for that tenant's licensed data."}],"servers":[{"url":"{protocol}://{domain}/{basePath}","variables":{"protocol":{"enum":["http","https"],"default":"https"},"domain":{"default":"api.studio.fabricdata.com"},"basePath":{"default":"v1"}}}],"security":[{"jwt-apikey-authorizer":[]}],"components":{"securitySchemes":{"jwt-apikey-authorizer":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"CreateLicensedMetadataConfigRequest":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ICreateLicensedMetadataConfigRequest","description":"The request to enable a licensed metadata provider for a tenant. A tenant may only have one config per provider.","type":"object","additionalProperties":false,"required":["data"],"properties":{"meta":{"$ref":"#/components/schemas/WriteRequestMeta"},"data":{"type":"object","additionalProperties":false,"required":["type","attributes"],"properties":{"type":{"type":"string","enum":["licensedMetadataConfigs"]},"attributes":{"$ref":"#/components/schemas/CreateLicensedMetadataConfigAttributes"}}}}},"WriteRequestMeta":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IWriteRequestMeta","description":"Optional audit metadata for a write request. `audit.source` is the issuing surface/channel (e.g. studio-web, mcp, api, system); `audit.reason` is a free-text justification. Both are optional and length-capped.","type":"object","additionalProperties":false,"properties":{"audit":{"type":"object","additionalProperties":false,"properties":{"source":{"type":"string","maxLength":64},"reason":{"type":"string","maxLength":256}}}}},"CreateLicensedMetadataConfigAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ICreateLicensedMetadataConfigAttributes","description":"Attributes for enabling a licensed metadata provider for a tenant. The tenant's own provider API key is required — Fabric never shares a key across tenants.","type":"object","additionalProperties":false,"required":["provider","programsPollingIntervalMinutes","taxonomyPollingInterval"],"properties":{"provider":{"type":"string","enum":["gracenote"],"description":"The licensed metadata provider to enable."},"apiKey":{"type":"string","description":"Write-only. The tenant's own provider API key. Stored securely (per-tenant secret) and never returned in responses. Required unless apiKeyArn is provided."},"apiKeyArn":{"type":"string","description":"Reference to a previously stored API key. Use this instead of apiKey if the key is already registered."},"programsPollingIntervalMinutes":{"type":"integer","enum":[15,30,45,60],"description":"How often to check for new program updates once caught up on the initial seed. This calls the provider's API using the tenant's own entitlement, so the tenant chooses the tradeoff between freshness and their own API quota usage — there is no default."},"taxonomyPollingInterval":{"type":"string","enum":["daily","weekly"],"description":"How often to check for changes to the provider's reference taxonomy (a small, rarely-changing dataset). Also drawn from the tenant's own API quota — there is no default."}}},"LicensedMetadataConfigCreateOneResponse":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","title":"ILicensedMetadataConfigCreateOneResponse","description":"Response after enabling a licensed metadata provider for a tenant. Contains the new resource and self link.","additionalProperties":false,"required":["data","links"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/BaseResource"},{"type":"object","properties":{"attributes":{"allOf":[{"$ref":"#/components/schemas/BaseResourceAttributes"},{"$ref":"#/components/schemas/LicensedMetadataConfigAttributes"}]},"meta":{"type":"object","description":"Non-attribute reference info. seedJobId isn't a JSON:API relationship because there's no dedicated backgroundJobs relationship endpoint here — check its status via GET /backgroundJobs/{jobId}.","properties":{"seedJobId":{"type":"string","description":"ID of the background job that ran (or is running) the initial seed, if one has been started."}},"additionalProperties":false}},"required":["attributes"],"additionalProperties":false}]},"links":{"type":"object","properties":{"self":{"type":"string"}},"required":["self"],"additionalProperties":false}}},"BaseResource":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResource","description":"JSON:API resource identifier. All Origin Studio API resource objects include at least an `id` (unique string identifier) and a `type` (the JSON:API resource type name, e.g. records, contributors).","type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"}},"required":["id","type"],"additionalProperties":false},"BaseResourceAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResourceAttributes","description":"Common timestamps present on all resource attribute objects. `created` is the ISO 8601 date-time when the resource was first created; `updated` is the date-time of the most recent modification.","type":"object","additionalProperties":false,"required":["created","updated"],"properties":{"created":{"type":"string","format":"date-time"},"updated":{"type":"string","format":"date-time"}}},"LicensedMetadataConfigAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataConfigAttributes","description":"Per-tenant licensed metadata provider config (read-only view). Existence of the resource means the provider is enabled for this tenant; delete disables it. Shows which provider is configured and whether an API key is stored — the raw key is never returned, only a partial preview.","type":"object","additionalProperties":false,"required":["provider","apiKeyConfigured","programsPollingIntervalMinutes","taxonomyPollingInterval","seedingStatus"],"properties":{"provider":{"type":"string","enum":["gracenote"],"description":"The licensed metadata provider this config enables for the tenant."},"apiKeyConfigured":{"type":"boolean","description":"Whether an API key has been securely stored for this provider."},"apiKeyPreview":{"type":"string","description":"First 2 and last 2 characters of the stored API key (e.g. \"gc••••9a\"), for display only. Never enough to reconstruct the key. Absent until an API key has been stored at least once."},"programsPollingIntervalMinutes":{"type":"integer","enum":[15,30,45,60],"description":"How often program updates are checked for once caught up on the initial seed."},"taxonomyPollingInterval":{"type":"string","enum":["daily","weekly"],"description":"How often the provider's reference taxonomy is checked for changes."},"pollingPaused":{"type":"boolean","description":"True when the tenant has paused steady-state polling. While paused, both feeds' schedules are disabled and a steady-state drain already in flight stops at its next page, so no further provider API quota is spent on polling. An initial seed job already running is NOT stopped and will finish its crawl. Cursors, cached data and the configured cadence are all retained — resuming continues from where polling stopped rather than re-crawling. Absent means not paused."},"seedingStatus":{"type":"string","enum":["not_started","seeding","finished","failed"],"description":"Status of the initial data seed. Ongoing polling only starts once this reaches 'finished'."},"seedingError":{"type":"string","description":"Customer-safe reason the initial seed failed. Present only when seedingStatus is 'failed'."},"seedingStartedAt":{"type":"string","format":"date-time","description":"When the most recent initial seed was launched. Recorded on this resource rather than read back from the seed's background-job row, because those rows are deleted by a 7-day TTL while this timestamp has to stay visible for the life of the integration. Absent until a seed has been launched at least once."},"seedingFinishedAt":{"type":"string","format":"date-time","description":"When the most recent initial seed reached a terminal state, whether it succeeded or failed. Absent while a seed is running, and for seeds launched before this field existed."},"polling":{"type":"object","additionalProperties":false,"description":"Steady-state polling health per feed, so a tenant can tell whether ongoing ingestion is working after the initial seed finishes. A feed key is absent until that feed has been polled at least once.","properties":{"programs":{"$ref":"#/components/schemas/LicensedMetadataFeedHealth"},"taxonomy":{"$ref":"#/components/schemas/LicensedMetadataFeedHealth"}}},"sandbox":{"type":"boolean","description":"True when this config is using the Fabric sandbox API key, so its cached records are synthetic test data rather than licensed Gracenote content. Sandbox titles also carry a TMSId prefix Gracenote never issues (ZZ...), so they stay identifiable outside this response. Read-only — derived from the key, not settable."}}},"LicensedMetadataFeedHealth":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataFeedHealth","description":"Steady-state health of one licensed-metadata feed's polling. Present once the feed has been polled at least once.","type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","enum":["pending","ok","unauthorized","error"],"description":"Outcome of the most recent poll. 'pending' means the feed is scheduled but hasn't completed a poll yet; 'ok' succeeded; 'unauthorized' means the provider rejected the API key — rotate it via PATCH; 'error' is any other failure."},"lastPolledAt":{"type":"string","description":"ISO timestamp of the most recent poll attempt, whatever its outcome."},"lastSuccessAt":{"type":"string","description":"ISO timestamp of the most recent successful poll. A stale value here while status is not 'ok' means ingestion is currently broken."},"nextPollAt":{"type":"string","description":"Estimated ISO timestamp of the next poll — lastPolledAt plus this feed's configured interval. An estimate: the actual schedule fires on a fixed cadence, so a change to the interval only takes effect from the next tick."},"caughtUp":{"type":"boolean","description":"Whether the feed had no further pending updates as of the last successful poll."},"stalled":{"type":"boolean","description":"Present and true when the feed is overdue — the current time is well past nextPollAt, meaning the scheduler appears to have stopped firing rather than the feed being merely idle. A stalled feed's status/message reflect its last known outcome, which may look healthy; treat stalled as a distinct 'polling has silently stopped' signal. Absent when the feed is polling on schedule."},"behindBy":{"type":"integer","description":"Approximate number of update-stream positions still pending as of the last successful poll (0 when caught up)."},"message":{"type":"string","description":"Human-readable detail, present for non-ok statuses — what went wrong and what to do about it."}}},"ErrorResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IErrorResponse","description":"Standard JSON:API error response returned when a request fails. Contains an `errors` array with one or more error objects (status, title, and optional detail).","type":"object","required":["errors"],"additionalProperties":false,"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"}}}},"Error":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IError","description":"A single JSON:API error object. `status` is the HTTP status code as a string. `title` is a short, human-readable summary. `detail` provides optional context. `meta` may include structured conflict or authorization details.","type":"object","additionalProperties":false,"required":["status","title"],"properties":{"status":{"type":"string"},"title":{"type":"string"},"detail":{"type":"string"},"source":{"type":"object","description":"A JSON Pointer to the request field that caused the error (e.g. `/data/attributes/title`).","properties":{"pointer":{"type":"string"}}},"meta":{"type":"object","additionalProperties":true,"properties":{"stack":{"type":"string","description":"Error stack trace. Only present in non-production environments."},"conflictItems":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}},"unauthorizedItems":{"type":"array","items":{"type":"string"}}}}}}}},"paths":{"/global/licensedMetadataConfigs":{"post":{"summary":"Enable a licensed metadata provider for a tenant","operationId":"licensedMetadataConfigCreateOne","description":"Enable a licensed third-party metadata provider (e.g. Gracenote) for the caller's tenant, using the tenant's own API key. A tenant may only have one config per provider.","tags":["Licensed Metadata"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateLicensedMetadataConfigRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LicensedMetadataConfigCreateOneResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict — a config already exists for this tenant and provider","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Disable a licensed metadata provider config

> Disable a licensed metadata provider config by its ID, scoped to the caller's tenant. The stored API key is deleted immediately. Previously cached data for this provider is not deleted by this call.

```json
{"openapi":"3.0.3","info":{"title":"Fabric API - Audit","version":"0.9.2264"},"tags":[{"name":"Licensed Metadata","description":"Enable or disable a licensed third-party metadata provider (e.g. Gracenote) for a tenant, using the tenant's own API key. Unlike Global Metadata, this API performs real per-tenant mutations and provisions dedicated, isolated storage for that tenant's licensed data."}],"servers":[{"url":"{protocol}://{domain}/{basePath}","variables":{"protocol":{"enum":["http","https"],"default":"https"},"domain":{"default":"api.studio.fabricdata.com"},"basePath":{"default":"v1"}}}],"security":[{"jwt-apikey-authorizer":[]}],"components":{"securitySchemes":{"jwt-apikey-authorizer":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"ErrorResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IErrorResponse","description":"Standard JSON:API error response returned when a request fails. Contains an `errors` array with one or more error objects (status, title, and optional detail).","type":"object","required":["errors"],"additionalProperties":false,"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"}}}},"Error":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IError","description":"A single JSON:API error object. `status` is the HTTP status code as a string. `title` is a short, human-readable summary. `detail` provides optional context. `meta` may include structured conflict or authorization details.","type":"object","additionalProperties":false,"required":["status","title"],"properties":{"status":{"type":"string"},"title":{"type":"string"},"detail":{"type":"string"},"source":{"type":"object","description":"A JSON Pointer to the request field that caused the error (e.g. `/data/attributes/title`).","properties":{"pointer":{"type":"string"}}},"meta":{"type":"object","additionalProperties":true,"properties":{"stack":{"type":"string","description":"Error stack trace. Only present in non-production environments."},"conflictItems":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}},"unauthorizedItems":{"type":"array","items":{"type":"string"}}}}}}}},"paths":{"/global/licensedMetadataConfigs/{licensedMetadataConfigId}":{"delete":{"summary":"Disable a licensed metadata provider config","operationId":"licensedMetadataConfigDeleteOne","description":"Disable a licensed metadata provider config by its ID, scoped to the caller's tenant. The stored API key is deleted immediately. Previously cached data for this provider is not deleted by this call.","tags":["Licensed Metadata"],"parameters":[{"name":"licensedMetadataConfigId","description":"The ID of the licensed metadata config to disable.","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Update a licensed metadata provider config (e.g. rotate the API key)

> Update an existing licensed metadata provider config by its ID, scoped to the caller's tenant. Use this to rotate the tenant's provider API key (write-only) or change the polling cadence WITHOUT tearing down the existing cache, cursors, or bucket. Only the attributes supplied are changed; omitted attributes are left as-is.

```json
{"openapi":"3.0.3","info":{"title":"Fabric API - Audit","version":"0.9.2264"},"tags":[{"name":"Licensed Metadata","description":"Enable or disable a licensed third-party metadata provider (e.g. Gracenote) for a tenant, using the tenant's own API key. Unlike Global Metadata, this API performs real per-tenant mutations and provisions dedicated, isolated storage for that tenant's licensed data."}],"servers":[{"url":"{protocol}://{domain}/{basePath}","variables":{"protocol":{"enum":["http","https"],"default":"https"},"domain":{"default":"api.studio.fabricdata.com"},"basePath":{"default":"v1"}}}],"security":[{"jwt-apikey-authorizer":[]}],"components":{"securitySchemes":{"jwt-apikey-authorizer":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"UpdateLicensedMetadataConfigRequest":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IUpdateLicensedMetadataConfigRequest","description":"The request to update an existing licensed metadata provider config (e.g. rotate the API key). The config is identified by the path parameter; the body carries only the attributes to change.","type":"object","additionalProperties":false,"required":["data"],"properties":{"meta":{"$ref":"#/components/schemas/WriteRequestMeta"},"data":{"type":"object","additionalProperties":false,"required":["type","attributes"],"properties":{"type":{"type":"string","enum":["licensedMetadataConfigs"]},"attributes":{"$ref":"#/components/schemas/UpdateLicensedMetadataConfigAttributes"}}}}},"WriteRequestMeta":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IWriteRequestMeta","description":"Optional audit metadata for a write request. `audit.source` is the issuing surface/channel (e.g. studio-web, mcp, api, system); `audit.reason` is a free-text justification. Both are optional and length-capped.","type":"object","additionalProperties":false,"properties":{"audit":{"type":"object","additionalProperties":false,"properties":{"source":{"type":"string","maxLength":64},"reason":{"type":"string","maxLength":256}}}}},"UpdateLicensedMetadataConfigAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IUpdateLicensedMetadataConfigAttributes","description":"Attributes for updating an existing licensed metadata provider config. Every attribute is optional — supply only what you want to change. The provider itself is immutable and cannot be changed here.","type":"object","additionalProperties":false,"properties":{"apiKey":{"type":"string","description":"Write-only. A new provider API key to rotate to. Stored securely (per-tenant secret, overwriting the previous value) and never returned in responses. Rotating the key does not tear down the existing cache."},"apiKeyArn":{"type":"string","description":"Reference to a previously stored API key to point at instead. Mutually exclusive with apiKey."},"programsPollingIntervalMinutes":{"type":"integer","enum":[15,30,45,60],"description":"How often to check for new program updates once caught up on the initial seed."},"taxonomyPollingInterval":{"type":"string","enum":["daily","weekly"],"description":"How often to check for changes to the provider's reference taxonomy."},"pollingPaused":{"type":"boolean","description":"Set true to pause steady-state polling, false to resume. Pausing disables both feeds' schedules and stops a steady-state drain already in flight at its next page; an initial seed job already running is not affected and will finish. Nothing is deleted — cursors, cached data and the configured cadence are retained, so resuming continues from where polling stopped. Changing the cadence while paused keeps it paused."}}},"LicensedMetadataConfigCreateOneResponse":{"$schema":"http://json-schema.org/draft-07/schema#","type":"object","title":"ILicensedMetadataConfigCreateOneResponse","description":"Response after enabling a licensed metadata provider for a tenant. Contains the new resource and self link.","additionalProperties":false,"required":["data","links"],"properties":{"data":{"allOf":[{"$ref":"#/components/schemas/BaseResource"},{"type":"object","properties":{"attributes":{"allOf":[{"$ref":"#/components/schemas/BaseResourceAttributes"},{"$ref":"#/components/schemas/LicensedMetadataConfigAttributes"}]},"meta":{"type":"object","description":"Non-attribute reference info. seedJobId isn't a JSON:API relationship because there's no dedicated backgroundJobs relationship endpoint here — check its status via GET /backgroundJobs/{jobId}.","properties":{"seedJobId":{"type":"string","description":"ID of the background job that ran (or is running) the initial seed, if one has been started."}},"additionalProperties":false}},"required":["attributes"],"additionalProperties":false}]},"links":{"type":"object","properties":{"self":{"type":"string"}},"required":["self"],"additionalProperties":false}}},"BaseResource":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResource","description":"JSON:API resource identifier. All Origin Studio API resource objects include at least an `id` (unique string identifier) and a `type` (the JSON:API resource type name, e.g. records, contributors).","type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"}},"required":["id","type"],"additionalProperties":false},"BaseResourceAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResourceAttributes","description":"Common timestamps present on all resource attribute objects. `created` is the ISO 8601 date-time when the resource was first created; `updated` is the date-time of the most recent modification.","type":"object","additionalProperties":false,"required":["created","updated"],"properties":{"created":{"type":"string","format":"date-time"},"updated":{"type":"string","format":"date-time"}}},"LicensedMetadataConfigAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataConfigAttributes","description":"Per-tenant licensed metadata provider config (read-only view). Existence of the resource means the provider is enabled for this tenant; delete disables it. Shows which provider is configured and whether an API key is stored — the raw key is never returned, only a partial preview.","type":"object","additionalProperties":false,"required":["provider","apiKeyConfigured","programsPollingIntervalMinutes","taxonomyPollingInterval","seedingStatus"],"properties":{"provider":{"type":"string","enum":["gracenote"],"description":"The licensed metadata provider this config enables for the tenant."},"apiKeyConfigured":{"type":"boolean","description":"Whether an API key has been securely stored for this provider."},"apiKeyPreview":{"type":"string","description":"First 2 and last 2 characters of the stored API key (e.g. \"gc••••9a\"), for display only. Never enough to reconstruct the key. Absent until an API key has been stored at least once."},"programsPollingIntervalMinutes":{"type":"integer","enum":[15,30,45,60],"description":"How often program updates are checked for once caught up on the initial seed."},"taxonomyPollingInterval":{"type":"string","enum":["daily","weekly"],"description":"How often the provider's reference taxonomy is checked for changes."},"pollingPaused":{"type":"boolean","description":"True when the tenant has paused steady-state polling. While paused, both feeds' schedules are disabled and a steady-state drain already in flight stops at its next page, so no further provider API quota is spent on polling. An initial seed job already running is NOT stopped and will finish its crawl. Cursors, cached data and the configured cadence are all retained — resuming continues from where polling stopped rather than re-crawling. Absent means not paused."},"seedingStatus":{"type":"string","enum":["not_started","seeding","finished","failed"],"description":"Status of the initial data seed. Ongoing polling only starts once this reaches 'finished'."},"seedingError":{"type":"string","description":"Customer-safe reason the initial seed failed. Present only when seedingStatus is 'failed'."},"seedingStartedAt":{"type":"string","format":"date-time","description":"When the most recent initial seed was launched. Recorded on this resource rather than read back from the seed's background-job row, because those rows are deleted by a 7-day TTL while this timestamp has to stay visible for the life of the integration. Absent until a seed has been launched at least once."},"seedingFinishedAt":{"type":"string","format":"date-time","description":"When the most recent initial seed reached a terminal state, whether it succeeded or failed. Absent while a seed is running, and for seeds launched before this field existed."},"polling":{"type":"object","additionalProperties":false,"description":"Steady-state polling health per feed, so a tenant can tell whether ongoing ingestion is working after the initial seed finishes. A feed key is absent until that feed has been polled at least once.","properties":{"programs":{"$ref":"#/components/schemas/LicensedMetadataFeedHealth"},"taxonomy":{"$ref":"#/components/schemas/LicensedMetadataFeedHealth"}}},"sandbox":{"type":"boolean","description":"True when this config is using the Fabric sandbox API key, so its cached records are synthetic test data rather than licensed Gracenote content. Sandbox titles also carry a TMSId prefix Gracenote never issues (ZZ...), so they stay identifiable outside this response. Read-only — derived from the key, not settable."}}},"LicensedMetadataFeedHealth":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILicensedMetadataFeedHealth","description":"Steady-state health of one licensed-metadata feed's polling. Present once the feed has been polled at least once.","type":"object","additionalProperties":false,"required":["status"],"properties":{"status":{"type":"string","enum":["pending","ok","unauthorized","error"],"description":"Outcome of the most recent poll. 'pending' means the feed is scheduled but hasn't completed a poll yet; 'ok' succeeded; 'unauthorized' means the provider rejected the API key — rotate it via PATCH; 'error' is any other failure."},"lastPolledAt":{"type":"string","description":"ISO timestamp of the most recent poll attempt, whatever its outcome."},"lastSuccessAt":{"type":"string","description":"ISO timestamp of the most recent successful poll. A stale value here while status is not 'ok' means ingestion is currently broken."},"nextPollAt":{"type":"string","description":"Estimated ISO timestamp of the next poll — lastPolledAt plus this feed's configured interval. An estimate: the actual schedule fires on a fixed cadence, so a change to the interval only takes effect from the next tick."},"caughtUp":{"type":"boolean","description":"Whether the feed had no further pending updates as of the last successful poll."},"stalled":{"type":"boolean","description":"Present and true when the feed is overdue — the current time is well past nextPollAt, meaning the scheduler appears to have stopped firing rather than the feed being merely idle. A stalled feed's status/message reflect its last known outcome, which may look healthy; treat stalled as a distinct 'polling has silently stopped' signal. Absent when the feed is polling on schedule."},"behindBy":{"type":"integer","description":"Approximate number of update-stream positions still pending as of the last successful poll (0 when caught up)."},"message":{"type":"string","description":"Human-readable detail, present for non-ok statuses — what went wrong and what to do about it."}}},"ErrorResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IErrorResponse","description":"Standard JSON:API error response returned when a request fails. Contains an `errors` array with one or more error objects (status, title, and optional detail).","type":"object","required":["errors"],"additionalProperties":false,"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"}}}},"Error":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IError","description":"A single JSON:API error object. `status` is the HTTP status code as a string. `title` is a short, human-readable summary. `detail` provides optional context. `meta` may include structured conflict or authorization details.","type":"object","additionalProperties":false,"required":["status","title"],"properties":{"status":{"type":"string"},"title":{"type":"string"},"detail":{"type":"string"},"source":{"type":"object","description":"A JSON Pointer to the request field that caused the error (e.g. `/data/attributes/title`).","properties":{"pointer":{"type":"string"}}},"meta":{"type":"object","additionalProperties":true,"properties":{"stack":{"type":"string","description":"Error stack trace. Only present in non-production environments."},"conflictItems":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}},"unauthorizedItems":{"type":"array","items":{"type":"string"}}}}}}}},"paths":{"/global/licensedMetadataConfigs/{licensedMetadataConfigId}":{"patch":{"summary":"Update a licensed metadata provider config (e.g. rotate the API key)","operationId":"licensedMetadataConfigUpdateOne","description":"Update an existing licensed metadata provider config by its ID, scoped to the caller's tenant. Use this to rotate the tenant's provider API key (write-only) or change the polling cadence WITHOUT tearing down the existing cache, cursors, or bucket. Only the attributes supplied are changed; omitted attributes are left as-is.","tags":["Licensed Metadata"],"parameters":[{"name":"licensedMetadataConfigId","description":"The ID of the licensed metadata config to update.","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateLicensedMetadataConfigRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LicensedMetadataConfigCreateOneResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```

## Start a licensed metadata config's initial data seed

> Starts the initial data seed for a licensed metadata provider config as a background job. This can take a long time to complete depending on the provider's entitlement size. Ongoing polling for new updates only begins once the seed finishes successfully. Only one seed job may run at a time per config.

```json
{"openapi":"3.0.3","info":{"title":"Fabric API - Audit","version":"0.9.2264"},"tags":[{"name":"Licensed Metadata","description":"Enable or disable a licensed third-party metadata provider (e.g. Gracenote) for a tenant, using the tenant's own API key. Unlike Global Metadata, this API performs real per-tenant mutations and provisions dedicated, isolated storage for that tenant's licensed data."}],"servers":[{"url":"{protocol}://{domain}/{basePath}","variables":{"protocol":{"enum":["http","https"],"default":"https"},"domain":{"default":"api.studio.fabricdata.com"},"basePath":{"default":"v1"}}}],"security":[{"jwt-apikey-authorizer":[]}],"components":{"securitySchemes":{"jwt-apikey-authorizer":{"type":"apiKey","name":"Authorization","in":"header"}},"schemas":{"BackgroundJobFindOneResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBackgroundJobFindOneResponse","description":"JSON:API response for a single background job.","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["backgroundJobs"]},"attributes":{"$ref":"#/components/schemas/BackgroundJobAttributes"},"links":{"type":"object","properties":{"self":{"type":"string"}},"additionalProperties":false},"relationships":{"type":"object","properties":{"tenant":{"$ref":"#/components/schemas/TenantRelationship"},"author":{"$ref":"#/components/schemas/UserRelationship"}},"additionalProperties":false}},"required":["id","type","attributes"],"additionalProperties":false}},"required":["data"],"additionalProperties":false},"BackgroundJobAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBackgroundJobAttributes","description":"Attributes for a background job. Background jobs represent long-running asynchronous operations (e.g. contributor deletion, tenant data export). Includes current status, job type, parameters, optional output on completion, optional error details on failure, and optional item progress counts for batch jobs.","type":"object","allOf":[{"$ref":"#/components/schemas/BaseResourceAttributes"},{"type":"object","properties":{"type":{"type":"string"},"parameters":{"type":"object"},"status":{"$ref":"#/components/schemas/BackgroundJobStatus"},"output":{"type":"object"},"totalItemsCount":{"type":"integer","minimum":0,"description":"Total number of items in a batch job, when applicable."},"successItemsCount":{"type":"integer","minimum":0,"description":"Number of items processed successfully in a batch job, when applicable."},"failedItemsCount":{"type":"integer","minimum":0,"description":"Number of items that failed in a batch job, when applicable."},"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"}},"required":["message","code"],"additionalProperties":false}},"required":["type","parameters","status"],"additionalProperties":false}],"additionalProperties":false},"BaseResourceAttributes":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBaseResourceAttributes","description":"Common timestamps present on all resource attribute objects. `created` is the ISO 8601 date-time when the resource was first created; `updated` is the date-time of the most recent modification.","type":"object","additionalProperties":false,"required":["created","updated"],"properties":{"created":{"type":"string","format":"date-time"},"updated":{"type":"string","format":"date-time"}}},"BackgroundJobStatus":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IBackgroundJobStatus","description":"Status of a background job: ABORTED, FAILED, PENDING_REDRIVE, RUNNING, SUCCEEDED, or TIMED_OUT.","type":"string","enum":["ABORTED","FAILED","PENDING_REDRIVE","RUNNING","SUCCEEDED","TIMED_OUT"]},"TenantRelationship":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ITenantRelationship","description":"A relationship to a tenant","type":"object","properties":{"data":{"additionalProperties":false,"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["tenants"]}},"required":["id","type"]},"links":{"$ref":"#/components/schemas/LinksRelated"}},"required":["data","links"],"additionalProperties":false},"LinksRelated":{"$schema":"http://json-schema.org/draft-07/schema#","title":"ILinks","description":"A JSON:API links object with an absolute or relative 'related' link","type":"object","required":["related"],"properties":{"related":{"type":"string"}},"additionalProperties":false},"UserRelationship":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IUserRelationship","description":"A relationship to the actor behind a write (e.g. `author`, `updatedBy`). `data.meta.type` is the actor type — a human user, an API key, or an internal service — and `links.related` is only present for human users, since API keys and services have no user resource to link to.","type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["users"]},"meta":{"type":"object","properties":{"tenantId":{"type":"string"},"type":{"type":"string"},"displayName":{"type":"string"}},"required":["tenantId","type","displayName"],"additionalProperties":false}},"required":["id","type","meta"],"additionalProperties":false},"links":{"$ref":"#/components/schemas/LinksRelated"}},"required":["data"],"additionalProperties":false},"ErrorResponse":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IErrorResponse","description":"Standard JSON:API error response returned when a request fails. Contains an `errors` array with one or more error objects (status, title, and optional detail).","type":"object","required":["errors"],"additionalProperties":false,"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"}}}},"Error":{"$schema":"http://json-schema.org/draft-07/schema#","title":"IError","description":"A single JSON:API error object. `status` is the HTTP status code as a string. `title` is a short, human-readable summary. `detail` provides optional context. `meta` may include structured conflict or authorization details.","type":"object","additionalProperties":false,"required":["status","title"],"properties":{"status":{"type":"string"},"title":{"type":"string"},"detail":{"type":"string"},"source":{"type":"object","description":"A JSON Pointer to the request field that caused the error (e.g. `/data/attributes/title`).","properties":{"pointer":{"type":"string"}}},"meta":{"type":"object","additionalProperties":true,"properties":{"stack":{"type":"string","description":"Error stack trace. Only present in non-production environments."},"conflictItems":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}}}},"unauthorizedItems":{"type":"array","items":{"type":"string"}}}}}}}},"paths":{"/global/licensedMetadataConfigs/{licensedMetadataConfigId}/seedJob":{"post":{"summary":"Start a licensed metadata config's initial data seed","operationId":"licensedMetadataConfigSeedJobCreateOne","description":"Starts the initial data seed for a licensed metadata provider config as a background job. This can take a long time to complete depending on the provider's entitlement size. Ongoing polling for new updates only begins once the seed finishes successfully. Only one seed job may run at a time per config.","tags":["Licensed Metadata"],"parameters":[{"name":"licensedMetadataConfigId","description":"The ID of the licensed metadata config to seed.","in":"path","required":true,"schema":{"type":"string"}},{"name":"maxIterations","description":"Testing/diagnostic use only. Hard-stops the seed after this many pages regardless of end-of-stream, instead of draining the full entitlement. Omit for normal use.","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackgroundJobFindOneResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict — a seed job is already running for this config","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```
