Install
$ agentstack add skill-wardawgmalvicious-claude-config-fabric-rest-api ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Fabric REST API patterns
Base URL: https://api.fabric.microsoft.com/v1
Finding Workspaces and Items
- List workspaces:
GET /v1/workspaces→ paginate withcontinuationToken - Find by name: iterate results until
displayNamematches - List items in workspace:
GET /v1/workspaces/{wsId}/items?type={ItemType} - Type-specific endpoints return extra
properties(connection strings, etc.):
GET /v1/workspaces/{wsId}/{lakehouses|warehouses|notebooks|semanticModels|...}
- Cross-workspace discovery (no
wsIdin path):POST /v1/catalog/searchwith{ "search": "Sales Revenue", "pageSize": 50, "filter": "Type eq 'Lakehouse'" }→ permission-trimmed items (each withidGUID +workspaceId) across your whole accessible estate, by catalog metadata (name/description/workspace name). OneLake Catalog Search API (Preview, March 2026); same capability as thefab findCLI verb (fabric-cli skill) and the Fabric Core MCP server'ssearch_catalogtool.
Sensitivity Labels on Item Responses
As of March 2026 GA, List Items / Get Item / Update Item responses include a sensitivityLabel property on the Item object when the item has a Purview information-protection label applied:
{
"id": "34fa05fc-e7ba-435a-b1ab-63534b6aa4f9",
"displayName": "PDF_Env",
"type": "Environment",
"workspaceId": "2ae4b0b9-895b-4676-bfcd-125b1f20e34d",
"sensitivityLabel": { "sensitivityLabelId": "4922deb9-fe99-4f4f-971c-9bdf556ec6a9" }
}
Field name gotcha — spec vs runtime drift. The MS Learn Item definition page documents this inner field as id, but the live API returns sensitivityLabelId. Verified across 233 labeled items in 7 workspaces (May 2026) on both GET /v1/workspaces/{wsId}/items and GET /v1/workspaces/{wsId}/items/{itemId} — 233/233 use sensitivityLabelId. Trust the runtime, not the schema page.
The sensitivityLabel object carries only the label GUID — there is no displayName / name on the response. Resolving the GUID to a human-readable label name requires a separate call (Microsoft Graph /security/informationProtection/sensitivityLabels or Purview). The sensitivityLabel field is omitted entirely on unlabeled items.
Read-only on these endpoints. The Update Item PATCH body schema (UpdateItemRequest) accepts only displayName and description — it does NOT accept a sensitivityLabel field. To apply, change, or remove labels programmatically, call the admin APIs POST /v1/admin/labels/bulkSetLabels / bulkRemoveLabels (require Tenant.ReadWrite.All + Fabric admin role; ≤ 25 req/hour, ≤ 2000 items/request).
Item IDs: Runtime vs logicalId
Every Fabric item has two GUIDs, and they are NOT interchangeable. Using the wrong one is a leading cause of PowerBIEntityNotFound / EntityNotFound errors when referencing items by ID from a pipeline, notebook parameter, REST call, or Variable Library variable.
| Identifier | Source | Resolved by | Use for | |---|---|---|---| | Runtime item ID | Fabric portal URL (…/items/{guid}); GET /v1/workspaces/{wsId}/items response id | The Fabric engine at execution time | Pipeline activity notebookId / pipelineId, Variable Library values consumed at runtime, REST /items/{id} calls, notebookutils.runtime.context lookups | | logicalId | .platform file → config.logicalId | Git integration / source-control tooling | .platform payloads, PBIP + pbir-cli + fab import source-control operations |
The two values can look visually similar — Git integration sometimes derives one as a byte-swapped permutation of the other (segments reshuffled) — but Fabric will NOT silently fall back from one to the other at runtime.
Rule of thumb: if the value ends up in a payload the Fabric engine reads (pipeline definition, Variable Library value, REST body), use the runtime item ID. If it ends up in a file the Git integration layer reads (.platform, PBIP manifest), use the logicalId.
To fetch the runtime ID:
- Portal: open the item, copy the trailing GUID from the URL
- REST:
GET /v1/workspaces/{wsId}/items→ find bydisplayName, returnid fabCLI:fab get "Workspace.Workspace/Item.ItemType" -q id
Variable Library specifically: variable string values are passed verbatim to consumers — they are NOT resolved against .platform logicalIds. Always store the runtime item ID.
Long-Running Operations (LRO)
Many mutating calls return 202 Accepted with Location and x-ms-operation-id headers.
- Poll:
GET /v1/operations/{operationId}→status: Running|Succeeded|Failed - Get result:
GET /v1/operations/{operationId}/result(after Succeeded) - Honor
Retry-Afterheader. Use exponential backoff if absent.
Item creation is 201-OR-202, not "always sync" or "always LRO" — the same endpoint can return either depending on current provisioning load. Always branch on status code; never assume sync. A function that only inspects the body on 2xx will silently return before the item is queryable whenever Fabric picks the LRO path, racing any immediately-downstream call that expects the new item to exist.
Canonical create_* pattern for Fabric item creation:
def create_item(access_token: str, item_name: str, ...) -> str:
"""Return the newly-created item's GUID. Handles 201 and 202."""
workspace_id = notebookutils.runtime.context["currentWorkspaceId"]
endpoint = f"https://api.fabric.microsoft.com/v1/workspaces/{workspace_id}/"
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
body = {"displayName": item_name}
response = requests.post(endpoint, headers=headers, json=body)
if response.status_code == 201:
# Synchronous creation — the body is the created item
item_id = response.json()["id"]
elif response.status_code == 202:
# LRO — poll to completion, then fetch /result for the item body
result_response = poll_lro(response, headers, return_result=True)
item_id = result_response.json()["id"]
else:
response.raise_for_status()
return item_id
The poll_lro helper honors Retry-After, polls Location until the operation reaches Succeeded, and (with return_result=True) fetches /result to return the actual created-item body.
Definition APIs
Item types with content (Notebook, SemanticModel, Report, DataPipeline) accept a definition object containing format, parts, and base64-encoded payloads.
PATCH /v1/workspaces/{wsId}/items/{itemId}/definition[?updateMetadata=true]
?updateMetadata=truecontrols whether.platformis honored. Without it, any.platformpart indefinition.partsis silently ignored —displayName/descriptionwon't update.- Conversely, setting
?updateMetadata=truerequires a.platformpart. The flag without.platformreturns 400. - For content-only updates (typical), omit the flag.
- Notebook-specific alternative:
POST /v1/workspaces/{wsId}/notebooks/{id}/updateDefinition— same body shape. getDefinitionis a POST with empty body ('{}') — bare POST returns HTTP 411 Length Required. Result is fetched via the LRO's{Location}/resultURL (note the/resultsuffix).- Per-item-type specifics live in
fabric-tmdl-api(semantic models),fabric-spark(notebooks, SparkJobDefinition, Environment, Lakehouse shortcuts),fabric-eventhouse(Eventhouse, KQLDatabase),fabric-variable-library(VariableLibrary), and pbir-cli/pbir-report-workflow (Report).
Support matrix
| Item Type | Formats | Main parts | |---|---|---| | Notebook | ipynb (default), FabricGitSource | notebook-content.ipynb or .py/.sql/.scala/.r | | DataPipeline | (default) | pipeline-content.json | | SemanticModel | TMSL, TMDL (prefer) | definition/database.tmdl, model.tmdl, tables/*.tmdl, definition.pbism | | Report | PBIR-Legacy, PBIR (prefer) | definition/report.json, version.json, pages/*, definition.pbir | | Lakehouse | LakehouseDefinitionV1 | lakehouse.metadata.json (+ optional shortcuts.metadata.json) | | SparkJobDefinition | SparkJobDefinitionV1, SparkJobDefinitionV2 | SparkJobDefinitionV1.json (+ Main/, Libs/ for V2) | | Environment | (default — set null) | Libraries/PublicLibraries/environment.yml, Setting/Sparkcompute.yml | | VariableLibrary | omit format entirely | variables.json, settings.json, valueSets/.json | | Eventhouse | JSON | EventhouseProperties.json | | KQLDatabase | JSON | DatabaseProperties.json (+ optional DatabaseSchema.kql) | | Other (KQLDashboard, CopyJob, Dataflow, Eventstream, MirroredDatabase, GraphQLApi, etc.) | varies | see MS schema index |
definition.pbir byConnection only: Fabric REST API supports only byConnection semantic-model references in PBIR. The byPath form (used locally with pbir-cli) is not accepted by the Fabric REST endpoints — switch to byConnection before deploying.
Job Execution
POST /v1/workspaces/{wsId}/items/{itemId}/jobs/instances?jobType={jobType}
| Item Type | jobType | |---|---| | Notebook | RunNotebook | | DataPipeline | Pipeline | | SparkJobDefinition | SparkJob | | SemanticModel | Refresh |
Gotcha: DefaultJob does NOT work for most item types. Use the type-specific value above.
Job Scheduling
POST /v1/workspaces/{wsId}/items/{itemId}/jobs/{jobType}/schedules
URL path is /jobs/{jobType}/schedules — NOT /jobs/instances/schedules. Body:
{
"enabled": true,
"configuration": {
"startDateTime": "2026-01-01T06:00:00.000Z",
"endDateTime": "2027-01-01T06:00:00.000Z",
"type": "Daily",
"interval": 1,
"localTimeZoneId": "UTC",
"times": ["06:00"]
}
}
enabled, configuration.startDateTime, configuration.endDateTime, and configuration.type are all required. Omitting endDateTime returns 400. type ∈ Daily | Weekly | Monthly; Weekly also requires weekDays (array of day names). List with GET .../schedules; delete with DELETE .../schedules/{scheduleId}.
Rate Limiting
429 Too Many Requests includes a Retry-After header (seconds). Honor it; never tight-loop. Use exponential backoff with jitter when Retry-After is absent.
| Category | Approximate limit | |---|---| | Admin APIs | 200 req/hour per principal per tenant | | OneLake (ADLS) | Standard Azure Storage throttling, per workspace | | Warehouse TDS | ~128 concurrent connections (varies by SKU) |
Watch x-ms-ratelimit-remaining-* response headers to back off before hitting the limit.
Capacity
GET /v1/capacities— list available capacities. Filterstate == "Active".POST /v1/workspaces/{wsId}/assignToCapacitywith{ "capacityId": "..." }— assigns workspace to capacity.- Lakehouse, Warehouse, Notebook execution, and Spark Job Definitions all require workspace capacity. Without it, item creation returns
FeatureNotAvailable. - After assignment, verify
GET /v1/workspaces/{wsId}showscapacityAssignmentProgress: "Completed"before creating capacity-dependent items.
Pagination
All list APIs use continuation-token pagination. Keep calling with ?continuationToken={value} until the token is null. The response also includes continuationUri — the fully-formed next-page URL — which you can call directly without re-constructing the query string. The token itself is opaque; never modify it.
Reference
- Microsoft Learn: Item management overview
- Microsoft Learn: Long-running operations
- Microsoft Learn: Throttling
- Comprehensive MS Learn link bundle (concept / CRUD / LRO / pagination / throttling / jobs / item-specific / workspace / Git+CI/CD / MCP): [references/REFERENCE.md](references/REFERENCE.md)
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: wardawgmalvicious
- Source: wardawgmalvicious/claude-config
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.