From fd1ce18ffaccff8db66391a7b9b5fd62603f0c5d Mon Sep 17 00:00:00 2001 From: Amin Chirazi Date: Wed, 29 Jul 2026 06:03:29 +0000 Subject: [PATCH] feat: generate response types from the API spec Every route method was annotated `-> Dict` or `-> List[Dict]`, which tells a type checker nothing and an IDE less. The API's OpenAPI document now describes 209 of its 240 operations precisely, so the shapes are generated from it rather than hand-written - the arrangement datamaker-js already uses, and the one that stops an SDK drifting behind its API. spec/openapi.json -> src/datamaker/generated/schema.py -> src/datamaker/types.py 93 methods across 15 route modules now return a named type. TypedDicts, not pydantic: methods still return the plain dicts `response.json()` produces, so `project["name"]` works exactly as before and no existing code breaks. The types are checker-only. WHY THERE IS AN ALIAS MODULE. datamodel-code-generator lets an inline schema claim a bare name and renames the real component: the class called `Template` is an unrelated nested object from DatabaseTemplatesProposal, while the actual Template entity is `Template2`. Six of 123 schemas were affected, including Plan, Scenario and Template - the three this SDK uses most. Importing those blind would have shipped exactly the failure this work exists to prevent: a wrong type that type-checks. So the generator now derives `types.py` by matching KEY SETS against the spec on every run, and fails if any schema stops resolving uniquely. Nothing hand-written ever names a numbered class. Three schemas still cannot be generated - SetDetail, PackInstallListItem and SchemaGraphNavigation are composed with allOf, which the generator merges away instead of emitting. Those methods keep `Dict`. The list is explicit in the script and the audit fails if it grows. mypy found two mis-annotations while this was being written, both fixed rather than silenced: `read_file_by_path` returns raw bytes, not the metadata row its endpoint answers with, and `generate_from_template_id` was writing a `quantity` key onto a fetched Template. The latter now copies instead of mutating - identical behaviour, minus writing a field back onto a response object that the API never sends. Net mypy: 8 errors -> 6. Nothing introduced; two pre-existing bugs in connections.py fixed (a bool assigned into a dict inferred str-valued). CI gains the drift check and, for the first time, actually runs the tests. Co-Authored-By: Claude Opus 5 --- .github/workflows/build.yaml | 12 + README.md | 35 + scripts/generate_schema.py | 206 + spec/openapi.json | 23551 ++++++++++++++++ src/datamaker/generated/__init__.py | 7 + src/datamaker/generated/schema.py | 1655 ++ src/datamaker/main.py | 13 +- src/datamaker/routes/api_keys.py | 9 +- src/datamaker/routes/connections.py | 9 +- src/datamaker/routes/custom_types.py | 34 +- src/datamaker/routes/export_and_validation.py | 5 +- src/datamaker/routes/folders_and_utils.py | 25 +- src/datamaker/routes/generation.py | 3 +- src/datamaker/routes/keymaps.py | 5 +- src/datamaker/routes/masking_policies.py | 9 +- src/datamaker/routes/plans.py | 9 +- src/datamaker/routes/projects.py | 11 +- src/datamaker/routes/scenario_files.py | 11 +- src/datamaker/routes/sets.py | 9 +- src/datamaker/routes/teams.py | 21 +- src/datamaker/routes/templates.py | 13 +- src/datamaker/routes/users.py | 22 +- src/datamaker/types.py | 136 + 23 files changed, 25720 insertions(+), 90 deletions(-) create mode 100644 scripts/generate_schema.py create mode 100644 spec/openapi.json create mode 100644 src/datamaker/generated/__init__.py create mode 100644 src/datamaker/generated/schema.py create mode 100644 src/datamaker/types.py diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fe3f0ae..8dd136c 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -32,6 +32,18 @@ jobs: run: | uv sync --all-extras --dev + # The types in src/datamaker/generated (and the verified aliases in + # src/datamaker/types.py) are produced from spec/openapi.json. Stale or + # hand-edited types mean the SDK describes an API that has moved on - + # the same drift this package's `-> Dict` signatures used to hide. + - name: Generated types match the spec + run: | + python scripts/generate_schema.py --check + + - name: Run tests + run: | + uv run --with pytest pytest -m "not integration" + - name: Build package run: | uv build diff --git a/README.md b/README.md index 441577a..f1074c1 100644 --- a/README.md +++ b/README.md @@ -66,3 +66,38 @@ See [`artifacts/README.md`](artifacts/README.md) for more details. ## Development & Contibutions See the [contributing.md](/CONTRIBUTING.md) guide for details on how to contribute to this project. + +## Types + +Response types are **generated** from the API's OpenAPI document, not hand-written: + +``` +spec/openapi.json -> src/datamaker/generated/schema.py -> src/datamaker/types.py +``` + +Import from `datamaker.types`, which resolves each spec schema name to the class +that really matches it: + +```python +from datamaker.types import Project, Plan, Template + +projects: list[Project] = dm.get_projects() +``` + +They are `TypedDict`s, so nothing changes at runtime — methods still return the +plain dicts `response.json()` produces, and `project["name"]` works exactly as +before. The types are checker-only. + +To regenerate after the API changes: + +```bash +python scripts/generate_schema.py +``` + +CI runs `python scripts/generate_schema.py --check` and fails if the committed +types drift from the spec. + +Three schemas cannot currently be generated (`SetDetail`, `PackInstallListItem`, +`SchemaGraphNavigation`): they are composed with `allOf`, which the generator +merges away rather than emitting as a named class. Methods returning those keep +`Dict`. The generator script lists them explicitly and fails if the set grows. diff --git a/scripts/generate_schema.py b/scripts/generate_schema.py new file mode 100644 index 0000000..6428f4e --- /dev/null +++ b/scripts/generate_schema.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +""" +Generate `src/datamaker/generated/schema.py` from `spec/openapi.json`. + +WHY THIS EXISTS: every route method in this package used to be annotated +`-> Dict`, which tells a type checker nothing and an IDE less. The API's own +OpenAPI document already describes 209 of its 240 operations precisely, so the +shapes are generated from it rather than hand-copied - the same arrangement +`datamaker-js` uses. Hand-copied types are how an SDK drifts behind its API. + +Two flags matter for the drift check to mean anything: + + --disable-timestamp the default header stamps the generation time, so two + runs of the same input would differ and `--check` would + fail every time. + a pinned generator output is only reproducible for a fixed version, so CI + installs the same one this file names. + +Usage: + python scripts/generate_schema.py # write the file + python scripts/generate_schema.py --check # fail if it is stale +""" + +from __future__ import annotations + +import json +import re +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SPEC = ROOT / "spec" / "openapi.json" +OUT = ROOT / "src" / "datamaker" / "generated" / "schema.py" +ALIASES = ROOT / "src" / "datamaker" / "types.py" + +# Pinned: the generator's output is only byte-stable for a fixed version, and +# an unpinned bump would surface as a mysterious CI failure on an unrelated PR. +GENERATOR = "datamodel-code-generator==0.71.0" + +# `typing.TypedDict` rather than pydantic on purpose. The client returns +# `response.json()` - plain dicts - so these annotate what is already there +# instead of changing what callers receive at runtime. Existing code that does +# `project["name"]` keeps working; it just gets checked now. +ARGS = [ + "--input-file-type", + "openapi", + "--output-model-type", + "typing.TypedDict", + "--target-python-version", + "3.11", + "--disable-timestamp", + "--use-standard-collections", + "--use-union-operator", +] + + +def generate(destination: Path) -> None: + destination.parent.mkdir(parents=True, exist_ok=True) + subprocess.run( + [ + "uvx", + "--from", + GENERATOR, + "datamodel-codegen", + "--input", + str(SPEC), + "--output", + str(destination), + *ARGS, + ], + check=True, + ) + + +# Names that cannot be recovered, with the reason. `allOf` composition (zod's +# `.extend()`) is merged away by the generator rather than emitted as a class, +# and a schema used only inside a union never gets one either. Listed here so +# the gap is explicit; the audit fails if this list grows. +KNOWN_UNRECOVERABLE = { + "SetDetail": "allOf composition (Set + createdByName) is merged away", + "PackInstallListItem": "allOf composition (PackInstall + installedByName)", + "SchemaGraphNavigation": "only referenced inside a union; no standalone class", +} + + +def write_aliases(schema_path: Path, aliases_path: Path) -> list[str]: + """Map each spec schema name to the generated class that really matches it. + + The generator gives inline path/property schemas priority for a bare name, + so `components.schemas.Plan` can land as `Plan1` while an unrelated nested + object takes `Plan`. Silently importing the wrong one is precisely the + "wrong type that type-checks" failure this package exists to avoid, so the + mapping is derived by comparing KEY SETS against the spec on every run and + the numbering never reaches hand-written code. + """ + spec = json.loads(SPEC.read_text()) + comps = spec["components"]["schemas"] + src = schema_path.read_text() + blocks = dict(re.findall(r"^class (\w+)\(TypedDict\):\n((?: .*\n)+)", src, re.M)) + keys = {n: set(re.findall(r"^ (\w+):", b, re.M)) for n, b in blocks.items()} + plain = set(re.findall(r"^(\w+): TypeAlias", src, re.M)) + + lines, missing = [], [] + for name in sorted(comps): + want = set((comps[name].get("properties") or {}).keys()) + if not want: + if name in blocks or name in plain: + lines.append(f"{name} = _s.{name}") + else: + missing.append(name) + continue + exact = [c for c, k in keys.items() if k == want] + if len(exact) == 1: + lines.append(f"{name} = _s.{exact[0]}") + elif name in exact: + lines.append(f"{name} = _s.{name}") + else: + missing.append(name) + + unexpected = sorted(set(missing) - set(KNOWN_UNRECOVERABLE)) + if unexpected: + print( + "\nThese spec schemas no longer map to a uniquely-matching generated " + "class:\n " + "\n ".join(unexpected) + + "\n\nEither the spec changed shape or the generator's naming did. Do NOT " + "\npaper over it by picking a numbered class by hand - confirm which class " + "\nreally matches, then update KNOWN_UNRECOVERABLE or fix the spec.\n", + file=sys.stderr, + ) + raise SystemExit(1) + + body = [ + '"""Spec schema names, resolved to the classes the generator produced.', + "", + "Generated by scripts/generate_schema.py - do not edit by hand.", + "", + "Import types from HERE, not from `generated.schema`: the generator lets an", + "inline schema take a bare name like `Plan` and renames the real component to", + "`Plan1`, so the numbering is not stable across spec changes. This module is", + "re-derived by matching key sets against the spec on every generation.", + '"""', + "", + "from .generated import schema as _s", + "", + ] + for name, why in sorted(KNOWN_UNRECOVERABLE.items()): + body.append(f"# {name}: {why}") + body.append("") + body.extend(lines) + body.append("") + aliases_path.write_text("\n".join(body)) + return missing + + +def main() -> int: + if not SPEC.exists(): + print(f"missing {SPEC.relative_to(ROOT)}", file=sys.stderr) + return 1 + + check = "--check" in sys.argv + + if not check: + generate(OUT) + missing = write_aliases(OUT, ALIASES) + classes = OUT.read_text().count("(TypedDict):") + exported = ALIASES.read_text().count(" = _s.") + print( + f"wrote {OUT.relative_to(ROOT)} ({classes} TypedDicts) and " + f"{ALIASES.relative_to(ROOT)} ({exported} names, {len(missing)} unrecoverable)" + ) + return 0 + + if not OUT.exists(): + print( + "src/datamaker/generated/schema.py is missing.\n" + "Run `python scripts/generate_schema.py` and commit it.", + file=sys.stderr, + ) + return 1 + + with tempfile.TemporaryDirectory() as tmp: + fresh_path = Path(tmp) / "schema.py" + generate(fresh_path) + fresh_aliases = Path(tmp) / "types.py" + write_aliases(fresh_path, fresh_aliases) + if ( + fresh_path.read_text() != OUT.read_text() + or fresh_aliases.read_text() != ALIASES.read_text() + ): + print( + "\nsrc/datamaker/generated/schema.py is stale or hand-edited.\n" + "Run `python scripts/generate_schema.py` and commit the result.\n" + "These types are generated from spec/openapi.json - editing them\n" + "by hand puts the SDK back to describing an API that has moved on.\n", + file=sys.stderr, + ) + return 1 + + print("generated types match the spec") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/spec/openapi.json b/spec/openapi.json new file mode 100644 index 0000000..5b8f222 --- /dev/null +++ b/spec/openapi.json @@ -0,0 +1,23551 @@ +{ + "components": { + "schemas": { + "AddressAvailability": { + "properties": { + "available": { + "type": "boolean" + } + }, + "required": [ + "available" + ], + "type": "object" + }, + "ApiError": { + "properties": { + "code": { + "type": "string" + }, + "details": { + "type": "string" + }, + "error": { + "type": "string" + } + }, + "required": [ + "error" + ], + "type": "object" + }, + "ApiKey": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "description": "The key value itself - secret", + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "role": { + "anyOf": [ + { + "enum": [ + "READ", + "READ_WRITE", + "SCENARIO" + ], + "type": "string" + }, + { + "type": "string" + } + ] + }, + "teamId": { + "type": [ + "string", + "null" + ] + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "userId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "key", + "name", + "role", + "createdAt", + "updatedAt", + "userId", + "teamId", + "projectId" + ], + "type": "object" + }, + "ApiKeyValidation": { + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "ApiMessageError": { + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "ApprovalDecision": { + "properties": { + "created": { + "type": "boolean" + }, + "decision": { + "enum": [ + "allow", + "pending" + ], + "type": "string" + }, + "id": { + "type": "string" + } + }, + "required": [ + "decision", + "id" + ], + "type": "object" + }, + "ApprovalStatus": { + "properties": { + "id": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": [ + "id", + "status" + ], + "type": "object" + }, + "AuditEvent": { + "properties": { + "category": { + "enum": [ + "READ", + "WRITE", + "DESTRUCTIVE", + "EXEC" + ], + "type": "string" + }, + "chatId": { + "description": "Correlation only; no FK, so audit survives chat deletion", + "type": [ + "string", + "null" + ] + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "decision": { + "enum": [ + "AUTO", + "ALLOWED", + "DENIED", + "APPROVED" + ], + "type": "string" + }, + "durationMs": { + "type": [ + "integer", + "null" + ] + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "finishedAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "inputDigest": { + "description": "Masked shape and hash of the arguments; never raw values", + "type": "null" + }, + "principal": { + "description": "Resolved server-side from the verified JWT", + "type": [ + "string", + "null" + ] + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "sdkSessionId": { + "type": [ + "string", + "null" + ] + }, + "startedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "status": { + "enum": [ + "ok", + "error" + ], + "type": "string" + }, + "targetKind": { + "type": [ + "string", + "null" + ] + }, + "targetRef": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": [ + "string", + "null" + ] + }, + "tool": { + "type": "string" + }, + "trackingId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "chatId", + "sdkSessionId", + "trackingId", + "teamId", + "projectId", + "principal", + "tool", + "category", + "targetKind", + "targetRef", + "decision", + "status", + "error", + "startedAt", + "finishedAt", + "durationMs", + "createdAt" + ], + "type": "object" + }, + "AuditEventRef": { + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + }, + "BuiltinSkill": { + "properties": { + "appliedCount": { + "type": "integer" + }, + "by": { + "type": "string" + }, + "description": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "description": "Prefixed \"builtin:\"", + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "const": "builtin", + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "id", + "slug", + "name", + "description", + "enabled", + "appliedCount", + "scope", + "by" + ], + "type": "object" + }, + "CapabilityCatalogItem": { + "properties": { + "group": { + "description": "The template folder's name, if any", + "type": "string" + }, + "hidden": { + "const": false, + "type": "boolean" + }, + "key": { + "description": "The template id", + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "type": "string" + }, + "source": { + "const": "derived", + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + } + }, + "required": [ + "key", + "name", + "source", + "hidden", + "templateId", + "template", + "note" + ], + "type": "object" + }, + "Chat": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "messages": { + "description": "The transcript; JSON column" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "title": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "title", + "createdBy", + "projectId", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "ChatAsset": { + "properties": { + "chatId": { + "type": "string" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "filename": { + "type": "string" + }, + "id": { + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "presignedUrl": { + "description": "Short-lived download URL", + "type": [ + "string", + "null" + ] + }, + "s3Key": { + "type": "string" + }, + "size": { + "type": [ + "integer", + "null" + ] + }, + "source": { + "description": "Who produced it, e.g. \"agent\"", + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "chatId", + "filename", + "s3Key", + "presignedUrl", + "size", + "mimeType", + "source", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "ClassifiedFields": { + "items": { + "additionalProperties": true, + "properties": { + "name": { + "type": "string" + }, + "sensitive": { + "description": "Absent when this field's classification call failed", + "type": "boolean" + } + }, + "type": "object" + }, + "type": "array" + }, + "ConnectionTable": { + "properties": { + "columns": { + "items": { + "properties": { + "column_name": { + "type": "string" + }, + "data_type": { + "type": "string" + } + }, + "required": [ + "column_name", + "data_type" + ], + "type": "object" + }, + "type": "array" + }, + "dependencies": { + "description": "Foreign keys pointing AT this table", + "items": { + "properties": { + "dependent_column": { + "type": "string" + }, + "dependent_table": { + "type": "string" + }, + "referenced_column": { + "type": "string" + } + }, + "required": [ + "dependent_table", + "dependent_column", + "referenced_column" + ], + "type": "object" + }, + "type": "array" + }, + "rows": { + "description": "One element; the row count is at `rows[0].total_count`", + "items": { + "properties": { + "total_count": {} + }, + "type": "object" + }, + "type": "array" + }, + "table_name": { + "type": "string" + } + }, + "required": [ + "table_name", + "columns", + "rows", + "dependencies" + ], + "type": "object" + }, + "ConnectionVerdict": { + "properties": { + "message": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "status": { + "description": "Upstream HTTP status, when one was received", + "type": "integer" + } + }, + "required": [ + "ok", + "message" + ], + "type": "object" + }, + "CsrfToken": { + "properties": { + "cookie": { + "description": "The full Cookie header value; absent when none was set", + "type": "string" + }, + "cookie_name": { + "type": "string" + }, + "cookie_value": { + "type": "string" + }, + "csrf_token": { + "type": "string" + } + }, + "required": [ + "cookie_name", + "cookie_value", + "csrf_token" + ], + "type": "object" + }, + "CsvBatchUploadError": { + "properties": { + "error": { + "type": "string" + }, + "ok": { + "const": false, + "type": "boolean" + } + }, + "required": [ + "ok", + "error" + ], + "type": "object" + }, + "CsvBatchUploadResult": { + "properties": { + "files": { + "items": { + "properties": { + "deduped": { + "type": "boolean" + }, + "key": { + "description": "The storage key", + "type": "string" + }, + "name": { + "description": "Sanitised filename, path stripped", + "type": "string" + }, + "url": { + "description": "Presigned download URL, valid 7 days", + "type": "string" + } + }, + "required": [ + "name", + "key", + "url", + "deduped" + ], + "type": "object" + }, + "type": "array" + }, + "folderName": { + "description": "As sent by the caller; null when none was given", + "type": [ + "string", + "null" + ] + }, + "folderPrefix": { + "description": "The storage prefix the files landed under", + "type": "string" + }, + "ok": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "ok", + "folderName", + "folderPrefix", + "files" + ], + "type": "object" + }, + "CurrentUser": { + "additionalProperties": true, + "properties": { + "avatar": { + "type": [ + "string", + "null" + ] + }, + "email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "picture": { + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "CustomDataType": { + "properties": { + "createdBy": { + "type": "string" + }, + "fieldConfig": { + "description": "Field configuration; JSON column" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "teamId", + "projectId", + "createdBy" + ], + "type": "object" + }, + "DatabaseExportResult": { + "properties": { + "command": { + "description": "e.g. \"INSERT\", \"SELECT\"", + "type": "string" + }, + "fields": { + "items": { + "properties": { + "columnID": { + "type": "integer" + }, + "dataTypeID": { + "description": "Postgres type OID", + "type": "integer" + }, + "dataTypeModifier": { + "type": "integer" + }, + "dataTypeSize": { + "type": "integer" + }, + "format": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tableID": { + "type": "integer" + } + }, + "required": [ + "name", + "tableID", + "columnID", + "dataTypeID", + "dataTypeSize", + "dataTypeModifier", + "format" + ], + "type": "object" + }, + "type": "array" + }, + "oid": { + "type": [ + "integer", + "null" + ] + }, + "rowCount": { + "type": [ + "integer", + "null" + ] + }, + "rows": { + "items": {}, + "type": "array" + } + }, + "required": [ + "command", + "rowCount", + "oid", + "rows", + "fields" + ], + "type": "object" + }, + "DatabaseTemplatesProposal": { + "properties": { + "ai_prompts": { + "description": "Per-table prompts for refining the proposal with the model", + "items": { + "additionalProperties": {}, + "type": "object" + }, + "type": "array" + }, + "data_generation_order": { + "description": "Table names in dependency order; null if a cycle was found", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] + }, + "templates": { + "items": { + "properties": { + "createdBy": { + "type": "string" + }, + "dbOrderIdx": { + "description": "Position in `data_generation_order`, or -1", + "type": "integer" + }, + "fields": { + "items": {}, + "type": "array" + }, + "name": { + "description": "The table name", + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "templateFolderId": { + "type": "string" + } + }, + "required": [ + "name", + "fields", + "templateFolderId", + "dbOrderIdx" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "templates", + "ai_prompts", + "data_generation_order" + ], + "type": "object" + }, + "DatamakerConfig": { + "properties": { + "aiGatewayUrl": { + "description": "\"\" when the server calls providers directly", + "type": "string" + }, + "allowedModels": { + "description": "An EMPTY array means no restriction, not none allowed", + "items": { + "type": "string" + }, + "type": "array" + }, + "apiContractVersion": { + "description": "So a desktop build can detect a major mismatch", + "type": "string" + }, + "defaultModel": { + "description": "\"\" when none is configured", + "type": "string" + }, + "opencodeUrl": { + "description": "\"\" to use the /opencode convention", + "type": "string" + }, + "quantityLimit": { + "description": "Max rows per generation call", + "type": "integer" + } + }, + "required": [ + "quantityLimit", + "aiGatewayUrl", + "opencodeUrl", + "defaultModel", + "allowedModels", + "apiContractVersion" + ], + "type": "object" + }, + "DeletedResult": { + "properties": { + "message": { + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + }, + "EffectivePermissions": { + "properties": { + "permissions": { + "items": { + "type": "string" + }, + "type": "array" + }, + "role": { + "description": "Base team role, null if not a member", + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "teamId", + "role", + "permissions" + ], + "type": "object" + }, + "Endpoint": { + "properties": { + "auth": { + "description": "Masked for ordinary callers", + "type": "null" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "headers": { + "description": "Masked for ordinary callers", + "type": "null" + }, + "id": { + "type": "string" + }, + "method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "queryParams": { + "type": "null" + }, + "teamId": { + "type": [ + "string", + "null" + ] + }, + "url": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "method", + "url", + "createdAt", + "createdBy", + "endpointFolderId", + "projectId", + "teamId" + ], + "type": "object" + }, + "EndpointFolder": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "createdAt", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "Feedback": { + "properties": { + "comment": { + "type": "string" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "feeling": { + "description": "FeedbackFeelings enum value", + "type": "string" + }, + "id": { + "type": "string" + } + }, + "required": [ + "id", + "comment", + "feeling", + "createdAt", + "createdBy" + ], + "type": "object" + }, + "FieldType": { + "properties": { + "meta": { + "additionalProperties": {}, + "description": "Display metadata for the field picker", + "type": "object" + }, + "options": { + "additionalProperties": {}, + "description": "Option keys differ per type", + "type": "object" + }, + "type": { + "description": "The generator name", + "type": "string" + } + }, + "required": [ + "type", + "options", + "meta" + ], + "type": "object" + }, + "GeneratedData": { + "properties": { + "dependencies": { + "additionalProperties": {}, + "description": "Resolved reference data used while generating", + "type": "object" + }, + "live_data": { + "additionalProperties": {}, + "description": "Generated values, keyed by field name", + "type": "object" + } + }, + "required": [ + "live_data", + "dependencies" + ], + "type": "object" + }, + "GeneratedTemplateField": { + "properties": { + "active": { + "type": "boolean" + }, + "function": { + "type": "string" + }, + "name": { + "type": "string" + }, + "nested": { + "items": { + "$ref": "#/components/schemas/GeneratedTemplateField" + }, + "type": "array" + }, + "optional": {}, + "options": { + "description": "Generator-specific settings" + }, + "textCase": { + "type": "string" + }, + "type": { + "description": "A DataMaker generator type", + "type": "string" + } + }, + "required": [ + "name", + "type" + ], + "type": "object" + }, + "GeneratedTemplateFields": { + "items": { + "$ref": "#/components/schemas/GeneratedTemplateField" + }, + "type": "array" + }, + "ImageAnalysisResult": { + "properties": { + "description": { + "description": "Model-generated description of the image", + "type": "string" + }, + "success": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "success", + "description" + ], + "type": "object" + }, + "Integration": { + "properties": { + "auth": { + "type": "null" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "headers": { + "type": "null" + }, + "id": { + "type": "string" + }, + "kind": { + "description": "e.g. \"sap\", \"tosca-cloud\", \"tosca-onprem\", \"jira\"", + "type": "string" + }, + "name": { + "type": "string" + }, + "origin": { + "description": "Base URL of the external system", + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "scope": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "name", + "kind", + "origin", + "scope", + "createdAt", + "createdBy", + "endpointFolderId", + "projectId", + "teamId" + ], + "type": "object" + }, + "KeyMapDeleteResult": { + "properties": { + "deleted": { + "description": "Entries removed", + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "message", + "deleted" + ], + "type": "object" + }, + "KeyMapEntriesPage": { + "properties": { + "entries": { + "items": { + "$ref": "#/components/schemas/KeyMapEntry" + }, + "type": "array" + }, + "mapName": { + "type": "string" + }, + "page": { + "description": "1-based", + "type": "integer" + }, + "pageSize": { + "type": "integer" + }, + "total": { + "description": "Entries matching the filter, not this page", + "type": "integer" + } + }, + "required": [ + "mapName", + "page", + "pageSize", + "total", + "entries" + ], + "type": "object" + }, + "KeyMapEntry": { + "properties": { + "newKey": { + "type": "string" + }, + "object": { + "type": "string" + }, + "oldKey": { + "type": "string" + }, + "runId": { + "type": [ + "string", + "null" + ] + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "object", + "oldKey", + "newKey", + "runId", + "updatedAt" + ], + "type": "object" + }, + "KeyMapLookupResult": { + "properties": { + "mapName": { + "type": "string" + }, + "mappings": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "missing": { + "items": { + "type": "string" + }, + "type": "array" + }, + "object": { + "type": "string" + } + }, + "required": [ + "mapName", + "object", + "mappings", + "missing" + ], + "type": "object" + }, + "KeyMapSummary": { + "properties": { + "entryCount": { + "type": "integer" + }, + "mapName": { + "type": "string" + }, + "object": { + "description": "The domain object type, e.g. Material", + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "mapName", + "object", + "entryCount", + "updatedAt" + ], + "type": "object" + }, + "KeyMapUpsertResult": { + "properties": { + "mapName": { + "type": "string" + }, + "object": { + "type": "string" + }, + "upserted": { + "description": "Rows inserted or updated", + "type": "integer" + } + }, + "required": [ + "mapName", + "object", + "upserted" + ], + "type": "object" + }, + "License": { + "properties": { + "captured": { + "description": "The verified claims, as captured at activation", + "type": "null" + }, + "expiresAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "issuedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "issuer": { + "type": "string" + }, + "keyId": { + "type": [ + "string", + "null" + ] + }, + "keyMasked": { + "description": "Masked rendering; never the usable key", + "type": "string" + }, + "offline": { + "type": "boolean" + }, + "revokedAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "seats": { + "type": [ + "integer", + "null" + ] + }, + "status": { + "description": "Derived: active, expired, revoked or superseded", + "type": "string" + }, + "supersededById": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": [ + "string", + "null" + ] + }, + "type": { + "description": "\"server\" or \"desktop\"", + "type": "string" + } + }, + "required": [ + "id", + "type", + "issuer", + "keyMasked", + "keyId", + "seats", + "offline", + "issuedAt", + "expiresAt", + "revokedAt", + "supersededById", + "teamId", + "status" + ], + "type": "object" + }, + "LicenseStatus": { + "properties": { + "code": { + "type": [ + "string", + "null" + ] + }, + "graceDays": { + "type": [ + "integer", + "null" + ] + }, + "graceEndsAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "issuer": { + "type": [ + "string", + "null" + ] + }, + "message": { + "type": [ + "string", + "null" + ] + }, + "mode": { + "description": "\"ok\" or a degraded mode", + "type": "string" + }, + "readOnlyReason": { + "type": [ + "string", + "null" + ] + }, + "required": { + "description": "Whether this deployment demands a license at all", + "type": "boolean" + } + }, + "required": [ + "required", + "mode", + "code", + "message", + "graceDays", + "graceEndsAt", + "issuer", + "readOnlyReason" + ], + "type": "object" + }, + "LogCleanupResult": { + "properties": { + "cutoffDate": { + "description": "ISO-8601 timestamp; logs older than this were removed", + "type": "string" + }, + "deletedCount": { + "type": "integer" + }, + "message": { + "type": "string" + } + }, + "required": [ + "message", + "deletedCount", + "cutoffDate" + ], + "type": "object" + }, + "LogoutResult": { + "properties": { + "ok": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + }, + "MaskingPolicy": { + "properties": { + "consistent": { + "type": "boolean" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "fields": {}, + "id": { + "type": "string" + }, + "keyMapName": { + "type": [ + "string", + "null" + ] + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "reversible": { + "type": "boolean" + }, + "teamId": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "consistent", + "reversible", + "keyMapName", + "createdAt", + "updatedAt", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "MemberInviteResult": { + "properties": { + "newMember": { + "allOf": [ + { + "$ref": "#/components/schemas/TeamMember" + } + ], + "properties": { + "firstName": { + "type": [ + "string", + "null" + ] + }, + "lastName": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "firstName", + "lastName" + ] + }, + "success": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "success", + "newMember" + ], + "type": "object" + }, + "MemberRoleAssignment": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "id": { + "type": "string" + }, + "roleId": { + "type": "string" + }, + "teamMemberId": { + "type": "string" + } + }, + "required": [ + "id", + "teamMemberId", + "roleId", + "createdAt" + ], + "type": "object" + }, + "PackCatalog": { + "additionalProperties": true, + "properties": { + "enabled": { + "type": "boolean" + }, + "packs": { + "items": {}, + "type": "array" + } + }, + "required": [ + "enabled" + ], + "type": "object" + }, + "PackCreatedCounts": { + "properties": { + "datatypes": { + "type": "integer" + }, + "plans": { + "type": "integer" + }, + "scenarios": { + "type": "integer" + }, + "skills": { + "type": "integer" + }, + "templates": { + "type": "integer" + } + }, + "required": [ + "templates", + "skills", + "plans", + "datatypes", + "scenarios" + ], + "type": "object" + }, + "PackExportResult": { + "properties": { + "pack": { + "properties": { + "assets": { + "properties": { + "datatypes": { + "default": [], + "items": { + "properties": { + "fieldConfig": {}, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array" + }, + "plans": { + "default": [], + "items": { + "properties": { + "spec": { + "properties": { + "approval": { + "properties": { + "reason": { + "type": "string" + }, + "role": { + "type": "string" + } + }, + "required": [ + "role", + "reason" + ], + "type": "object" + }, + "capabilities": { + "items": { + "properties": { + "expectedCount": { + "minimum": 0, + "type": "integer" + }, + "expectedSumCents": { + "type": "integer" + }, + "group": { + "type": "string" + }, + "hidden": { + "default": false, + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "setId": { + "type": "string" + }, + "source": { + "default": "derived", + "enum": [ + "derived", + "manual" + ], + "type": "string" + }, + "sumField": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + } + }, + "required": [ + "key", + "name", + "source", + "hidden", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "constraints": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "coverage": { + "items": { + "properties": { + "count": { + "minimum": 0, + "type": "integer" + }, + "family": { + "type": "string" + }, + "items": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "family", + "count", + "items" + ], + "type": "object" + }, + "type": "array" + }, + "coverageConfig": { + "properties": { + "freshnessDays": { + "default": 14, + "minimum": 1, + "type": "integer" + }, + "mode": { + "default": "release", + "enum": [ + "release", + "migration" + ], + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "sumToleranceCents": { + "default": 0, + "minimum": 0, + "type": "integer" + }, + "targetSystem": { + "type": "string" + } + }, + "required": [ + "mode", + "freshnessDays", + "sumToleranceCents" + ], + "type": "object" + }, + "entities": { + "default": [], + "items": { + "properties": { + "endpointId": { + "type": "string" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + }, + "volume": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "name", + "template", + "volume", + "key", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "expectations": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "flow": { + "items": { + "properties": { + "loop": { + "type": [ + "string", + "null" + ] + }, + "phase": { + "type": "string" + }, + "steps": { + "items": { + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "from": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "msg": { + "type": "string" + }, + "reachable": { + "type": "boolean" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "status": { + "enum": [ + "mapped", + "likely", + "custom", + "unknown" + ], + "type": "string" + }, + "to": { + "type": "string" + }, + "why": { + "type": "string" + } + }, + "required": [ + "from", + "to", + "msg", + "iface", + "kind", + "status" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "steps" + ], + "type": "object" + }, + "type": "array" + }, + "gaps": { + "default": [], + "items": { + "properties": { + "action": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "title": { + "type": "string" + }, + "type": { + "enum": [ + "connect", + "custom", + "input", + "blocker", + "gap", + "stale" + ], + "type": "string" + } + }, + "required": [ + "type", + "title", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "integrations": { + "default": [], + "items": { + "properties": { + "access": { + "enum": [ + "read", + "write" + ], + "type": "string" + }, + "detail": { + "default": "", + "type": "string" + }, + "system": { + "type": "string" + }, + "via": { + "type": "string" + } + }, + "required": [ + "system", + "via", + "access", + "detail" + ], + "type": "object" + }, + "type": "array" + }, + "kind": { + "enum": [ + "entity", + "flow", + "task", + "mapping" + ], + "type": "string" + }, + "lanes": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "lifecycle": { + "properties": { + "mode": { + "type": "string" + }, + "refresh": { + "type": "string" + }, + "replenish": { + "type": "string" + } + }, + "required": [ + "mode", + "refresh", + "replenish" + ], + "type": "object" + }, + "mappingConfig": { + "properties": { + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "maskingPolicyName": { + "type": "string" + }, + "sourceObject": { + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "targetObject": { + "type": "string" + }, + "targetSystem": { + "type": "string" + } + }, + "type": "object" + }, + "mappings": { + "items": { + "properties": { + "generate": { + "default": false, + "type": "boolean" + }, + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "sourceField": { + "type": "string" + }, + "status": { + "default": "open", + "enum": [ + "mapped", + "open", + "needs-review" + ], + "type": "string" + }, + "targetField": { + "type": "string" + }, + "transform": { + "default": "", + "type": "string" + } + }, + "required": [ + "targetField", + "transform", + "generate", + "status", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "steps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "tasks": { + "items": { + "properties": { + "note": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "tasks": { + "items": { + "properties": { + "deps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "detail": { + "default": "", + "type": "string" + }, + "key": { + "type": "string" + }, + "priority": { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "title", + "detail", + "deps" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "tasks" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "properties": { + "by": { + "type": "string" + }, + "n": { + "minimum": 1, + "type": "integer" + }, + "when": { + "type": "string" + } + }, + "required": [ + "n", + "by", + "when" + ], + "type": "object" + } + }, + "required": [ + "lanes", + "entities", + "gaps", + "integrations", + "constraints", + "steps", + "expectations" + ], + "type": "object" + }, + "summary": { + "type": "string" + }, + "title": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "title", + "spec" + ], + "type": "object" + }, + "type": "array" + }, + "scenarios": { + "default": [], + "items": { + "properties": { + "description": { + "type": "string" + }, + "files": { + "items": { + "properties": { + "content": { + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name", + "content" + ], + "type": "object" + }, + "minItems": 1, + "type": "array" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "params": { + "default": [], + "items": { + "properties": { + "default": { + "type": "string" + }, + "description": { + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "name", + "files", + "params" + ], + "type": "object" + }, + "type": "array" + }, + "skills": { + "default": [], + "items": { + "properties": { + "body": { + "minLength": 1, + "type": "string" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "slug": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "slug", + "name", + "description", + "body" + ], + "type": "object" + }, + "type": "array" + }, + "templates": { + "default": [], + "items": { + "properties": { + "fields": {}, + "name": { + "minLength": 1, + "type": "string" + }, + "seed": { + "type": "integer" + }, + "simulationConfig": {} + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "templates", + "skills", + "plans", + "datatypes", + "scenarios" + ], + "type": "object" + }, + "checksum": { + "type": "string" + }, + "format": { + "const": "dmpack@1", + "type": "string" + }, + "manifest": { + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "license": { + "type": "string" + }, + "name": { + "pattern": "^[a-z0-9][a-z0-9-]*\\/[a-z0-9][a-z0-9-]*$", + "type": "string" + }, + "publisher": { + "minLength": 1, + "type": "string" + }, + "requires": { + "default": [], + "items": { + "properties": { + "kind": { + "const": "integration", + "type": "string" + }, + "note": { + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "kind", + "type" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?$", + "type": "string" + } + }, + "required": [ + "name", + "version", + "description", + "publisher", + "requires" + ], + "type": "object" + }, + "publicKeyId": { + "type": "string" + }, + "signature": { + "type": "string" + } + }, + "required": [ + "format", + "manifest", + "assets" + ], + "type": "object" + }, + "warnings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "pack", + "warnings" + ], + "type": "object" + }, + "PackImportDiff": { + "properties": { + "containsScenarios": { + "type": "boolean" + }, + "entries": { + "items": { + "properties": { + "action": { + "enum": [ + "create", + "skip" + ], + "type": "string" + }, + "conflict": { + "description": "Only on skips: the existing asset's content differs", + "type": "boolean" + }, + "key": { + "description": "Template/plan/datatype/scenario name, or skill slug", + "type": "string" + }, + "kind": { + "enum": [ + "template", + "skill", + "plan", + "datatype", + "scenario" + ], + "type": "string" + } + }, + "required": [ + "kind", + "key", + "action" + ], + "type": "object" + }, + "type": "array" + }, + "missingRequirements": { + "items": { + "properties": { + "kind": { + "const": "integration", + "type": "string" + }, + "note": { + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "kind", + "type" + ], + "type": "object" + }, + "type": "array" + }, + "pack": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "name", + "version" + ], + "type": "object" + }, + "signature": { + "enum": [ + "valid", + "invalid", + "unsigned" + ], + "type": "string" + } + }, + "required": [ + "pack", + "entries", + "containsScenarios", + "missingRequirements", + "signature" + ], + "type": "object" + }, + "PackInstall": { + "properties": { + "created": { + "$ref": "#/components/schemas/PackCreatedCounts" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "id": { + "type": "string" + }, + "installedBy": { + "description": "A TeamMembers id", + "type": [ + "string", + "null" + ] + }, + "manifest": { + "properties": { + "createdAt": { + "type": "string" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "license": { + "type": "string" + }, + "name": { + "pattern": "^[a-z0-9][a-z0-9-]*\\/[a-z0-9][a-z0-9-]*$", + "type": "string" + }, + "publisher": { + "minLength": 1, + "type": "string" + }, + "requires": { + "default": [], + "items": { + "properties": { + "kind": { + "const": "integration", + "type": "string" + }, + "note": { + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "kind", + "type" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?$", + "type": "string" + } + }, + "required": [ + "name", + "version", + "description", + "publisher", + "requires" + ], + "type": "object" + }, + "name": { + "description": "`publisher/slug`", + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "signature": { + "description": "\"valid\" or \"unsigned\" at install time", + "type": "string" + }, + "source": { + "enum": [ + "catalog", + "file" + ], + "type": "string" + }, + "teamId": { + "type": [ + "string", + "null" + ] + }, + "version": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "version", + "manifest", + "source", + "signature", + "created", + "createdAt", + "installedBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "PackInstallListItem": { + "allOf": [ + { + "$ref": "#/components/schemas/PackInstall" + } + ], + "properties": { + "installedByName": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "installedByName" + ] + }, + "PackInstallResult": { + "properties": { + "created": { + "$ref": "#/components/schemas/PackCreatedCounts", + "description": "Absent on a dry run" + }, + "diff": { + "$ref": "#/components/schemas/PackImportDiff" + }, + "install": { + "$ref": "#/components/schemas/PackInstall", + "description": "Absent on a dry run" + } + }, + "required": [ + "diff" + ], + "type": "object" + }, + "PdfAnalysisResult": { + "properties": { + "documentInfo": { + "additionalProperties": {}, + "description": "Model-extracted fields; no key is guaranteed", + "type": "object" + }, + "downloadUrl": { + "description": "Presigned URL for the uploaded file", + "type": "string" + }, + "method": { + "description": "Which extraction path produced `documentInfo`", + "enum": [ + "text_extraction", + "image_analysis" + ], + "type": "string" + }, + "success": { + "const": true, + "type": "boolean" + }, + "textLength": { + "description": "Characters extracted; absent on the image path", + "type": "integer" + } + }, + "required": [ + "success", + "documentInfo", + "method", + "downloadUrl" + ], + "type": "object" + }, + "PermissionCatalogue": { + "properties": { + "all": { + "items": { + "type": "string" + }, + "type": "array" + }, + "grouped": { + "additionalProperties": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": "object" + } + }, + "required": [ + "all", + "grouped" + ], + "type": "object" + }, + "Plan": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "createdFrom": { + "type": [ + "string", + "null" + ] + }, + "env": { + "type": [ + "string", + "null" + ] + }, + "history": { + "items": { + "properties": { + "by": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "status": { + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "when": { + "type": "string" + } + }, + "required": [ + "status", + "when", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "id": { + "type": "string" + }, + "origin": { + "enum": [ + "chat", + "blueprint", + "intake", + "gap analysis", + null + ], + "type": [ + "string", + "null" + ] + }, + "owner": { + "type": [ + "string", + "null" + ] + }, + "projectId": { + "type": "string" + }, + "rows": { + "type": "integer" + }, + "spec": { + "properties": { + "approval": { + "properties": { + "reason": { + "type": "string" + }, + "role": { + "type": "string" + } + }, + "required": [ + "role", + "reason" + ], + "type": "object" + }, + "capabilities": { + "items": { + "properties": { + "expectedCount": { + "minimum": 0, + "type": "integer" + }, + "expectedSumCents": { + "type": "integer" + }, + "group": { + "type": "string" + }, + "hidden": { + "default": false, + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "setId": { + "type": "string" + }, + "source": { + "default": "derived", + "enum": [ + "derived", + "manual" + ], + "type": "string" + }, + "sumField": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + } + }, + "required": [ + "key", + "name", + "source", + "hidden", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "constraints": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "coverage": { + "items": { + "properties": { + "count": { + "minimum": 0, + "type": "integer" + }, + "family": { + "type": "string" + }, + "items": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "family", + "count", + "items" + ], + "type": "object" + }, + "type": "array" + }, + "coverageConfig": { + "properties": { + "freshnessDays": { + "default": 14, + "minimum": 1, + "type": "integer" + }, + "mode": { + "default": "release", + "enum": [ + "release", + "migration" + ], + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "sumToleranceCents": { + "default": 0, + "minimum": 0, + "type": "integer" + }, + "targetSystem": { + "type": "string" + } + }, + "required": [ + "mode", + "freshnessDays", + "sumToleranceCents" + ], + "type": "object" + }, + "entities": { + "default": [], + "items": { + "properties": { + "endpointId": { + "type": "string" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + }, + "volume": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "name", + "template", + "volume", + "key", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "expectations": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "flow": { + "items": { + "properties": { + "loop": { + "type": [ + "string", + "null" + ] + }, + "phase": { + "type": "string" + }, + "steps": { + "items": { + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "from": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "msg": { + "type": "string" + }, + "reachable": { + "type": "boolean" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "status": { + "enum": [ + "mapped", + "likely", + "custom", + "unknown" + ], + "type": "string" + }, + "to": { + "type": "string" + }, + "why": { + "type": "string" + } + }, + "required": [ + "from", + "to", + "msg", + "iface", + "kind", + "status" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "steps" + ], + "type": "object" + }, + "type": "array" + }, + "gaps": { + "default": [], + "items": { + "properties": { + "action": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "title": { + "type": "string" + }, + "type": { + "enum": [ + "connect", + "custom", + "input", + "blocker", + "gap", + "stale" + ], + "type": "string" + } + }, + "required": [ + "type", + "title", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "integrations": { + "default": [], + "items": { + "properties": { + "access": { + "enum": [ + "read", + "write" + ], + "type": "string" + }, + "detail": { + "default": "", + "type": "string" + }, + "system": { + "type": "string" + }, + "via": { + "type": "string" + } + }, + "required": [ + "system", + "via", + "access", + "detail" + ], + "type": "object" + }, + "type": "array" + }, + "kind": { + "enum": [ + "entity", + "flow", + "task", + "mapping" + ], + "type": "string" + }, + "lanes": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "lifecycle": { + "properties": { + "mode": { + "type": "string" + }, + "refresh": { + "type": "string" + }, + "replenish": { + "type": "string" + } + }, + "required": [ + "mode", + "refresh", + "replenish" + ], + "type": "object" + }, + "mappingConfig": { + "properties": { + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "maskingPolicyName": { + "type": "string" + }, + "sourceObject": { + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "targetObject": { + "type": "string" + }, + "targetSystem": { + "type": "string" + } + }, + "type": "object" + }, + "mappings": { + "items": { + "properties": { + "generate": { + "default": false, + "type": "boolean" + }, + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "sourceField": { + "type": "string" + }, + "status": { + "default": "open", + "enum": [ + "mapped", + "open", + "needs-review" + ], + "type": "string" + }, + "targetField": { + "type": "string" + }, + "transform": { + "default": "", + "type": "string" + } + }, + "required": [ + "targetField", + "transform", + "generate", + "status", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "steps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "tasks": { + "items": { + "properties": { + "note": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "tasks": { + "items": { + "properties": { + "deps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "detail": { + "default": "", + "type": "string" + }, + "key": { + "type": "string" + }, + "priority": { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "title", + "detail", + "deps" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "tasks" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "properties": { + "by": { + "type": "string" + }, + "n": { + "minimum": 1, + "type": "integer" + }, + "when": { + "type": "string" + } + }, + "required": [ + "n", + "by", + "when" + ], + "type": "object" + } + }, + "required": [ + "lanes", + "entities", + "gaps", + "integrations", + "constraints", + "steps", + "expectations" + ], + "type": "object" + }, + "status": { + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "summary": { + "type": [ + "string", + "null" + ] + }, + "targets": { + "items": { + "type": "string" + }, + "type": "array" + }, + "teamId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "write": { + "type": "boolean" + } + }, + "required": [ + "id", + "title", + "summary", + "status", + "origin", + "createdFrom", + "owner", + "env", + "write", + "rows", + "targets", + "spec", + "history", + "createdAt", + "updatedAt", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "PlanDeleteResult": { + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ], + "type": "object" + }, + "PlanRun": { + "properties": { + "completedAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "files": { + "description": "Only on GET /plans/:planId/runs/:runId", + "items": { + "$ref": "#/components/schemas/PlanRunFile" + }, + "type": "array" + }, + "id": { + "type": "string" + }, + "logs": { + "items": {}, + "type": "array" + }, + "mode": { + "description": "generate | release | migration", + "type": "string" + }, + "planId": { + "type": "string" + }, + "results": { + "description": "The graded check result; null on generate runs", + "properties": { + "config": { + "properties": { + "freshnessDays": { + "default": 14, + "minimum": 1, + "type": "integer" + }, + "mode": { + "default": "release", + "enum": [ + "release", + "migration" + ], + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "sumToleranceCents": { + "default": 0, + "minimum": 0, + "type": "integer" + }, + "targetSystem": { + "type": "string" + } + }, + "required": [ + "mode", + "freshnessDays", + "sumToleranceCents" + ], + "type": "object" + }, + "gaps": { + "default": [], + "items": { + "properties": { + "action": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "title": { + "type": "string" + }, + "type": { + "enum": [ + "connect", + "custom", + "input", + "blocker", + "gap", + "stale" + ], + "type": "string" + } + }, + "required": [ + "type", + "title", + "note" + ], + "type": "object" + }, + "type": "array" + }, + "items": { + "default": [], + "items": { + "properties": { + "checks": { + "default": [], + "items": { + "properties": { + "detail": { + "default": "", + "type": "string" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "status": { + "enum": [ + "pass", + "warn", + "fail", + "skip" + ], + "type": "string" + } + }, + "required": [ + "id", + "label", + "status", + "detail" + ], + "type": "object" + }, + "type": "array" + }, + "group": { + "type": "string" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "enum": [ + "ready", + "partial", + "missing", + "reconciled", + "discrepancy" + ], + "type": "string" + } + }, + "required": [ + "key", + "name", + "status", + "checks" + ], + "type": "object" + }, + "type": "array" + }, + "mode": { + "enum": [ + "release", + "migration" + ], + "type": "string" + }, + "summary": { + "properties": { + "failing": { + "minimum": 0, + "type": "integer" + }, + "partial": { + "minimum": 0, + "type": "integer" + }, + "passing": { + "minimum": 0, + "type": "integer" + }, + "scorePct": { + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "total": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "total", + "passing", + "partial", + "failing", + "scorePct" + ], + "type": "object" + }, + "verdict": { + "enum": [ + "ready", + "partial", + "blocked", + "reconciled", + "discrepancy" + ], + "type": "string" + } + }, + "required": [ + "mode", + "config", + "items", + "gaps", + "summary", + "verdict" + ], + "type": [ + "object", + "null" + ] + }, + "rows": { + "type": "integer" + }, + "source": { + "description": "\"manual\", \"agent\" or \"mcp\"", + "type": "string" + }, + "startedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "status": { + "description": "queued | running | completed | failed", + "type": "string" + }, + "verdict": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "planId", + "status", + "source", + "mode", + "rows", + "logs", + "results", + "verdict", + "error", + "startedAt", + "completedAt" + ], + "type": "object" + }, + "PlanRunAccepted": { + "properties": { + "runId": { + "type": "string" + }, + "status": { + "const": "running", + "type": "string" + } + }, + "required": [ + "runId", + "status" + ], + "type": "object" + }, + "PlanRunAck": { + "properties": { + "ok": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + }, + "PlanRunFile": { + "properties": { + "entity": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "id": { + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "presignedUrl": { + "type": [ + "string", + "null" + ] + }, + "size": { + "type": "integer" + } + }, + "required": [ + "id", + "entity", + "filename", + "size", + "mimeType", + "presignedUrl" + ], + "type": "object" + }, + "PlanRunFileAck": { + "properties": { + "file": { + "properties": { + "entity": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "description": "The S3 key", + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "size": { + "type": "integer" + } + }, + "required": [ + "id", + "entity", + "filename", + "key", + "size", + "mimeType" + ], + "type": "object" + }, + "ok": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "ok", + "file" + ], + "type": "object" + }, + "PlanSignoff": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "id": { + "type": "string" + }, + "note": { + "type": [ + "string", + "null" + ] + }, + "planId": { + "type": "string" + }, + "planRunId": { + "type": "string" + }, + "report": { + "description": "Immutable snapshot: run results + the definition of done" + }, + "reportHash": { + "description": "sha256 hex of the canonical report JSON", + "type": "string" + }, + "signedBy": { + "description": "Display name, stamped server-side", + "type": "string" + }, + "verdict": { + "description": "The covered run's verdict at sign-off time", + "type": "string" + } + }, + "required": [ + "id", + "planId", + "planRunId", + "signedBy", + "note", + "verdict", + "reportHash", + "createdAt" + ], + "type": "object" + }, + "PreferencesUpdateError": { + "properties": { + "error": { + "description": "Absent on the 401", + "type": "string" + }, + "ok": { + "const": false, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + }, + "PreferencesUpdateResult": { + "properties": { + "ok": { + "const": true, + "type": "boolean" + }, + "theme": { + "type": "string" + } + }, + "required": [ + "ok", + "theme" + ], + "type": "object" + }, + "PreviewResult": { + "properties": { + "data": { + "description": "Parsed JSON when `isJson`, raw text otherwise" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "The upstream RESPONSE headers, lowercased", + "type": "object" + }, + "isJson": { + "type": "boolean" + }, + "responseTime": { + "description": "Milliseconds", + "type": "integer" + }, + "status": { + "description": "The upstream HTTP status", + "type": "integer" + }, + "statusText": { + "type": "string" + } + }, + "required": [ + "status", + "statusText", + "headers", + "responseTime", + "isJson" + ], + "type": "object" + }, + "Project": { + "properties": { + "avatar": { + "type": [ + "string", + "null" + ] + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "avatar", + "description", + "createdAt", + "createdBy", + "teamId" + ], + "type": "object" + }, + "PythonExecutionError": { + "properties": { + "error": { + "type": "string" + }, + "executionTime": { + "type": "number" + }, + "jobId": { + "type": "string" + }, + "logs": { + "items": {}, + "type": "array" + }, + "result": {}, + "status": { + "type": "string" + }, + "success": { + "const": false, + "type": "boolean" + } + }, + "required": [ + "success", + "error" + ], + "type": "object" + }, + "PythonExecutionResult": { + "properties": { + "executionTime": { + "description": "Milliseconds", + "type": "number" + }, + "jobId": { + "type": "string" + }, + "logs": { + "items": {}, + "type": "array" + }, + "message": { + "description": "Only on the async path, where the job was merely queued", + "type": "string" + }, + "result": { + "description": "The worker's return value; absent on the async path" + }, + "status": { + "description": "\"completed\" on the sync paths; the queue state otherwise", + "type": "string" + }, + "success": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "success", + "jobId", + "status" + ], + "type": "object" + }, + "Role": { + "properties": { + "baseRole": { + "description": "TeamRole this role derives from, for system roles", + "type": [ + "string", + "null" + ] + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "isSystem": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "teamId", + "name", + "description", + "isSystem", + "baseRole", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "SapServiceCatalog": { + "properties": { + "activeCount": { + "type": "integer" + }, + "inactiveCount": { + "type": "integer" + }, + "probed": { + "type": "boolean" + }, + "probedCount": { + "type": "integer" + }, + "services": { + "items": { + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "metadataUrl": { + "type": "string" + }, + "serviceUrl": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "id", + "description", + "serviceUrl", + "metadataUrl" + ], + "type": "object" + }, + "type": "array" + }, + "status": { + "enum": [ + "active", + "inactive" + ], + "type": "string" + }, + "total": { + "description": "The count AFTER filtering and ?limit=", + "type": "integer" + }, + "truncated": { + "description": "The system has more services than one call will probe", + "type": "boolean" + } + }, + "required": [ + "services", + "total", + "probed" + ], + "type": "object" + }, + "Scenario": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "diagram": { + "type": [ + "string", + "null" + ] + }, + "diagramBusiness": { + "type": [ + "string", + "null" + ] + }, + "diagramError": { + "type": [ + "string", + "null" + ] + }, + "diagramSourceHash": { + "type": [ + "string", + "null" + ] + }, + "diagramStatus": { + "type": "string" + }, + "diagramUpdatedAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "environmentVariables": { + "description": "Name/value map; JSON column" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "presignedUrl": { + "description": "Short-lived S3 URL for the script", + "type": [ + "string", + "null" + ] + }, + "projectId": { + "type": "string" + }, + "requirementsUrl": { + "description": "Short-lived S3 URL for requirements.txt", + "type": [ + "string", + "null" + ] + }, + "storageUsed": { + "type": "integer" + }, + "teamId": { + "type": "string" + }, + "timeoutSeconds": { + "type": "integer" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "presignedUrl", + "requirementsUrl", + "storageUsed", + "timeoutSeconds", + "diagram", + "diagramBusiness", + "diagramStatus", + "diagramUpdatedAt", + "diagramError", + "diagramSourceHash", + "createdAt", + "updatedAt", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "ScenarioDiagramRegenerateAccepted": { + "properties": { + "status": { + "const": "pending", + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "ScenarioEnvironmentVariableList": { + "properties": { + "environmentVariables": { + "items": { + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "key", + "value" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "environmentVariables" + ], + "type": "object" + }, + "ScenarioEnvironmentVariables": { + "properties": { + "environmentVariables": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "environmentVariables" + ], + "type": "object" + }, + "ScenarioFile": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "filename": { + "type": "string" + }, + "folder": { + "enum": [ + "uploads", + "outputs" + ], + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "description": "Storage key; unique", + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "presignedUrl": { + "description": "Short-lived download URL", + "type": [ + "string", + "null" + ] + }, + "scenarioId": { + "type": "string" + }, + "size": { + "description": "Bytes", + "type": "integer" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "scenarioId", + "filename", + "key", + "presignedUrl", + "size", + "mimeType", + "folder", + "createdAt", + "updatedAt", + "createdBy" + ], + "type": "object" + }, + "ScenarioFileCreated": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "filePath": { + "description": "The stored `key`", + "type": "string" + }, + "folder": { + "description": "\"uploads\" or \"outputs\"", + "type": "string" + }, + "id": { + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "name": { + "description": "The stored `filename`", + "type": "string" + }, + "size": { + "description": "Bytes", + "type": "integer" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "name", + "size", + "mimeType", + "filePath", + "folder", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "ScenarioJob": { + "properties": { + "createdAt": { + "description": "Epoch milliseconds", + "type": [ + "number", + "null" + ] + }, + "finishedAt": { + "description": "Epoch milliseconds", + "type": [ + "number", + "null" + ] + }, + "jobId": { + "type": [ + "string", + "null" + ] + }, + "processedAt": { + "description": "Epoch milliseconds", + "type": [ + "number", + "null" + ] + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "scenarioId": { + "type": [ + "string", + "null" + ] + }, + "scenarioName": { + "type": [ + "string", + "null" + ] + }, + "state": { + "description": "e.g. \"active\", \"waiting\", \"completed\", \"failed\", \"cancelled\"", + "type": "string" + }, + "teamId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "jobId", + "state", + "scenarioId", + "scenarioName", + "projectId", + "teamId", + "createdAt", + "processedAt", + "finishedAt" + ], + "type": "object" + }, + "ScenarioJobStatus": { + "properties": { + "error": { + "type": [ + "string", + "null" + ] + }, + "logs": { + "properties": { + "count": { + "type": "integer" + }, + "logs": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "logs", + "count" + ], + "type": "object" + }, + "output": { + "type": [ + "string", + "null" + ] + }, + "progress": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + }, + { + "additionalProperties": {}, + "type": "object" + } + ], + "description": "BullMQ's job progress; always 0 on the local-run fallback" + }, + "state": { + "description": "Normalised: \"active\", \"completed\" or \"failed\" (see above)", + "type": "string" + } + }, + "required": [ + "state", + "progress", + "output", + "error", + "logs" + ], + "type": "object" + }, + "ScenarioJobs": { + "properties": { + "jobs": { + "items": { + "$ref": "#/components/schemas/ScenarioJob" + }, + "type": "array" + } + }, + "required": [ + "jobs" + ], + "type": "object" + }, + "ScenarioLog": { + "properties": { + "completedAt": { + "description": "ISO-8601 timestamp", + "type": [ + "string", + "null" + ] + }, + "createdBy": { + "type": "string" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "jobId": { + "description": "BullMQ job id; unique", + "type": "string" + }, + "logs": { + "description": "Normalised log lines", + "items": {}, + "type": "array" + }, + "output": { + "type": [ + "string", + "null" + ] + }, + "scenarioId": { + "type": "string" + }, + "source": { + "description": "How the run was started, e.g. \"manual\"", + "type": "string" + }, + "startedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "status": { + "description": "\"queued\", \"running\", \"completed\" or \"failed\"", + "type": "string" + } + }, + "required": [ + "id", + "scenarioId", + "jobId", + "status", + "source", + "logs", + "output", + "error", + "startedAt", + "completedAt", + "createdBy" + ], + "type": "object" + }, + "SchemaGraphChange": { + "anyOf": [ + { + "properties": { + "entity": { + "$ref": "#/components/schemas/SchemaGraphEntity" + }, + "kind": { + "const": "entity", + "type": "string" + }, + "status": { + "enum": [ + "added", + "removed" + ], + "type": "string" + } + }, + "required": [ + "kind", + "status", + "entity" + ], + "type": "object" + }, + { + "properties": { + "creatable": { + "properties": { + "cached": { + "type": "boolean" + }, + "live": { + "type": "boolean" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "entitySet": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "type": "object" + }, + "keys": { + "properties": { + "cached": { + "items": { + "type": "string" + }, + "type": "array" + }, + "live": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "kind": { + "const": "entity", + "type": "string" + }, + "label": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "name": { + "type": "string" + }, + "properties": { + "items": { + "anyOf": [ + { + "properties": { + "kind": { + "const": "property", + "type": "string" + }, + "property": { + "$ref": "#/components/schemas/SchemaGraphProperty" + }, + "status": { + "enum": [ + "added", + "removed" + ], + "type": "string" + } + }, + "required": [ + "kind", + "status", + "property" + ], + "type": "object" + }, + { + "properties": { + "changes": { + "properties": { + "creatable": { + "properties": { + "cached": { + "type": "boolean" + }, + "live": { + "type": "boolean" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "label": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "type": "object" + }, + "maxLength": { + "properties": { + "cached": { + "type": "integer" + }, + "live": { + "type": "integer" + } + }, + "type": "object" + }, + "nullable": { + "properties": { + "cached": { + "type": "boolean" + }, + "live": { + "type": "boolean" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "type": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "updatable": { + "properties": { + "cached": { + "type": "boolean" + }, + "live": { + "type": "boolean" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + } + }, + "type": "object" + }, + "kind": { + "const": "property", + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "const": "modified", + "type": "string" + } + }, + "required": [ + "kind", + "status", + "name", + "changes" + ], + "type": "object" + } + ] + }, + "type": "array" + }, + "status": { + "const": "modified", + "type": "string" + }, + "updatable": { + "properties": { + "cached": { + "type": "boolean" + }, + "live": { + "type": "boolean" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + } + }, + "required": [ + "kind", + "status", + "name", + "properties" + ], + "type": "object" + }, + { + "properties": { + "kind": { + "const": "navigation", + "type": "string" + }, + "navigation": { + "$ref": "#/components/schemas/SchemaGraphNavigation" + }, + "status": { + "enum": [ + "added", + "removed" + ], + "type": "string" + } + }, + "required": [ + "kind", + "status", + "navigation" + ], + "type": "object" + }, + { + "properties": { + "changes": { + "properties": { + "cardinality": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "to": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + } + }, + "type": "object" + }, + "from": { + "type": "string" + }, + "kind": { + "const": "navigation", + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "const": "modified", + "type": "string" + } + }, + "required": [ + "kind", + "status", + "from", + "name", + "changes" + ], + "type": "object" + } + ] + }, + "SchemaGraphConnectResult": { + "properties": { + "endpointId": { + "description": "The NEW bridge endpoint that was created", + "type": "string" + }, + "endpointName": { + "type": "string" + }, + "entity": { + "type": "string" + }, + "fieldsUpgraded": { + "type": "integer" + }, + "templateId": { + "type": "string" + }, + "upgraded": { + "description": "Names of the upgraded fields", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "templateId", + "endpointId", + "endpointName", + "entity", + "fieldsUpgraded", + "upgraded" + ], + "type": "object" + }, + "SchemaGraphDiffResult": { + "properties": { + "cachedRefreshedAt": { + "description": "ISO-8601 timestamp: when the cached graph was last refreshed", + "type": "string" + }, + "changes": { + "items": { + "$ref": "#/components/schemas/SchemaGraphChange" + }, + "type": "array" + }, + "drifted": { + "type": "boolean" + }, + "endpointId": { + "type": "string" + }, + "metadataUrl": { + "type": "string" + }, + "namespace": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "odataVersion": { + "properties": { + "cached": { + "type": "string" + }, + "live": { + "type": "string" + } + }, + "required": [ + "cached", + "live" + ], + "type": "object" + }, + "summary": { + "properties": { + "entitiesAdded": { + "type": "integer" + }, + "entitiesModified": { + "type": "integer" + }, + "entitiesRemoved": { + "type": "integer" + }, + "navigationsAdded": { + "type": "integer" + }, + "navigationsChanged": { + "type": "integer" + }, + "navigationsRemoved": { + "type": "integer" + }, + "propertiesAdded": { + "type": "integer" + }, + "propertiesChanged": { + "type": "integer" + }, + "propertiesRemoved": { + "type": "integer" + } + }, + "required": [ + "entitiesAdded", + "entitiesRemoved", + "entitiesModified", + "propertiesAdded", + "propertiesRemoved", + "propertiesChanged", + "navigationsAdded", + "navigationsRemoved", + "navigationsChanged" + ], + "type": "object" + } + }, + "required": [ + "endpointId", + "metadataUrl", + "cachedRefreshedAt", + "drifted", + "namespace", + "odataVersion", + "summary", + "changes" + ], + "type": "object" + }, + "SchemaGraphEntity": { + "properties": { + "creatable": { + "type": "boolean" + }, + "entitySet": { + "description": "Only when the type is bound", + "type": "string" + }, + "keys": { + "items": { + "type": "string" + }, + "type": "array" + }, + "kind": { + "const": "entity", + "type": "string" + }, + "label": { + "description": "sap:label, falling back to the name", + "type": "string" + }, + "name": { + "description": "EntityType name, e.g. \"SalesOrder\"", + "type": "string" + }, + "properties": { + "items": { + "$ref": "#/components/schemas/SchemaGraphProperty" + }, + "type": "array" + }, + "updatable": { + "type": "boolean" + } + }, + "required": [ + "kind", + "name", + "label", + "creatable", + "updatable", + "keys", + "properties" + ], + "type": "object" + }, + "SchemaGraphEntityContext": { + "properties": { + "entity": { + "$ref": "#/components/schemas/SchemaGraphEntity" + }, + "navigatedFrom": { + "items": { + "properties": { + "cardinality": { + "type": "string" + }, + "from": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "from", + "cardinality" + ], + "type": "object" + }, + "type": "array" + }, + "navigatesTo": { + "items": { + "properties": { + "cardinality": { + "type": "string" + }, + "name": { + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "name", + "to", + "cardinality" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "entity", + "navigatesTo", + "navigatedFrom" + ], + "type": "object" + }, + "SchemaGraphMinedField": { + "properties": { + "counts": { + "description": "Occurrence count per value, same order as `values`", + "items": { + "type": "integer" + }, + "type": "array" + }, + "distinct": { + "type": "integer" + }, + "enumLike": { + "description": "Few distinct values AND repeats observed: treat as a SAP code-list", + "type": "boolean" + }, + "name": { + "type": "string" + }, + "sampled": { + "description": "Non-empty values seen for this field", + "type": "integer" + }, + "values": { + "description": "Capped, most frequent first", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "name", + "sampled", + "distinct", + "values", + "counts", + "enumLike" + ], + "type": "object" + }, + "SchemaGraphNavigation": { + "properties": { + "cardinality": { + "description": "\"1\", \"0..1\" or \"*\"", + "type": "string" + }, + "from": { + "type": "string" + }, + "kind": { + "const": "navigation", + "type": "string" + }, + "name": { + "description": "NavigationProperty name on the source", + "type": "string" + }, + "to": { + "type": "string" + } + }, + "required": [ + "kind", + "from", + "to", + "name", + "cardinality" + ], + "type": "object" + }, + "SchemaGraphPathResult": { + "properties": { + "path": { + "items": { + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "via": { + "type": "string" + } + }, + "required": [ + "from", + "via", + "to" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "path" + ], + "type": "object" + }, + "SchemaGraphPreviewResult": { + "properties": { + "count": { + "type": "integer" + }, + "entity": { + "type": "string" + }, + "entitySet": { + "type": "string" + }, + "rows": { + "description": "Business fields only; OData bookkeeping keys are stripped", + "items": { + "additionalProperties": {}, + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "entity", + "entitySet", + "count", + "rows" + ], + "type": "object" + }, + "SchemaGraphProperty": { + "properties": { + "creatable": { + "type": "boolean" + }, + "label": { + "description": "From sap:label when present", + "type": "string" + }, + "maxLength": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "nullable": { + "type": "boolean" + }, + "type": { + "description": "EDM type, e.g. \"Edm.String\"", + "type": "string" + }, + "updatable": { + "type": "boolean" + } + }, + "required": [ + "name", + "type", + "nullable", + "creatable", + "updatable" + ], + "type": "object" + }, + "SchemaGraphRefreshResult": { + "properties": { + "entities": { + "type": "integer" + }, + "metadataUrl": { + "type": "string" + }, + "namespace": { + "type": "string" + }, + "navigations": { + "type": "integer" + }, + "odataVersion": { + "description": "\"2.0\" or \"4.0\"", + "type": "string" + }, + "schemaChanged": { + "description": "The EDMX hash moved since the last refresh; false on a first refresh", + "type": "boolean" + } + }, + "required": [ + "namespace", + "odataVersion", + "entities", + "navigations", + "schemaChanged", + "metadataUrl" + ], + "type": "object" + }, + "SchemaGraphSampleResult": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "$ref": "#/components/schemas/SchemaGraphMinedField" + }, + "type": "array" + }, + "rowsSampled": { + "type": "integer" + }, + "valueHelpSets": { + "description": "Entity sets that look like value-help / code-list sets", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "rowsSampled", + "fields", + "valueHelpSets" + ], + "type": "object" + }, + "SchemaGraphSearchResult": { + "properties": { + "results": { + "items": { + "properties": { + "entitySet": { + "type": "string" + }, + "label": { + "type": "string" + }, + "name": { + "type": "string" + }, + "score": { + "type": "integer" + } + }, + "required": [ + "name", + "label", + "score" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "results" + ], + "type": "object" + }, + "SchemaGraphTemplateResult": { + "properties": { + "entity": { + "type": "string" + }, + "fieldCount": { + "type": "integer" + }, + "id": { + "type": "string" + }, + "minedFields": { + "description": "Fields turned into Custom generators from sampled values; empty unless `useSamples` was sent", + "items": { + "type": "string" + }, + "type": "array" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "fieldCount", + "entity", + "minedFields" + ], + "type": "object" + }, + "SessionTokens": { + "properties": { + "accessToken": { + "description": "Short-lived JWT - secret", + "type": "string" + }, + "expiresIn": { + "description": "Access token lifetime, in seconds", + "type": "integer" + }, + "refreshExpiresAt": { + "description": "ISO-8601 timestamp the refresh token expires at", + "type": "string" + }, + "refreshToken": { + "description": "Rotated on every use - secret", + "type": "string" + } + }, + "required": [ + "accessToken", + "refreshToken", + "expiresIn", + "refreshExpiresAt" + ], + "type": "object" + }, + "Set": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "data": {}, + "description": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "locked": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "rowCount": { + "type": "integer" + }, + "teamId": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "name", + "description", + "rowCount", + "locked", + "createdAt", + "updatedAt", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "SetDetail": { + "allOf": [ + { + "$ref": "#/components/schemas/Set" + } + ], + "properties": { + "createdByName": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "createdByName" + ] + }, + "Shortcut": { + "properties": { + "context": { + "description": "ShortcutsContext enum value", + "type": "string" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "function": { + "description": "The action the binding triggers", + "type": "string" + }, + "id": { + "type": "string" + }, + "keys": { + "items": { + "type": "string" + }, + "type": "array" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "userId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "userId", + "function", + "context", + "keys", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "Skill": { + "discriminator": { + "mapping": { + "builtin": "#/components/schemas/BuiltinSkill", + "team": "#/components/schemas/TeamSkill" + }, + "propertyName": "scope" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/TeamSkill" + }, + { + "$ref": "#/components/schemas/BuiltinSkill" + } + ] + }, + "StringList": { + "items": { + "type": "string" + }, + "type": "array" + }, + "SuccessResult": { + "properties": { + "success": { + "type": "boolean" + } + }, + "required": [ + "success" + ], + "type": "object" + }, + "Team": { + "properties": { + "avatar": { + "type": [ + "string", + "null" + ] + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "name", + "avatar", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "TeamMember": { + "properties": { + "id": { + "type": "string" + }, + "role": { + "description": "TeamRole enum value, e.g. OWNER / ADMIN / MEMBER", + "type": "string" + }, + "teamId": { + "type": "string" + }, + "userId": { + "type": "string" + } + }, + "required": [ + "id", + "userId", + "teamId", + "role" + ], + "type": "object" + }, + "TeamSetupResult": { + "properties": { + "project": { + "$ref": "#/components/schemas/Project" + }, + "team": { + "$ref": "#/components/schemas/Team" + }, + "teamMember": { + "$ref": "#/components/schemas/TeamMember" + } + }, + "required": [ + "team", + "teamMember", + "project" + ], + "type": "object" + }, + "TeamSkill": { + "properties": { + "appliedCount": { + "type": "integer" + }, + "body": { + "description": "The SKILL.md body", + "type": "string" + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "description": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "scope": { + "const": "team", + "type": "string" + }, + "slug": { + "type": "string" + }, + "updatedAt": { + "description": "ISO-8601 timestamp", + "type": "string" + } + }, + "required": [ + "id", + "slug", + "name", + "description", + "body", + "enabled", + "appliedCount", + "scope", + "createdBy", + "createdAt", + "updatedAt" + ], + "type": "object" + }, + "Template": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "dbOrderIdx": { + "type": "integer" + }, + "fields": {}, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "seed": { + "type": [ + "integer", + "null" + ] + }, + "simulationConfig": { + "type": "null" + }, + "teamId": { + "type": [ + "string", + "null" + ] + }, + "templateFolderId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "name", + "dbOrderIdx", + "seed", + "createdAt", + "createdBy", + "templateFolderId", + "projectId", + "teamId" + ], + "type": "object" + }, + "TemplateFolder": { + "properties": { + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "createdBy": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "isDatabase": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "name", + "isDatabase", + "createdAt", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + }, + "ToscaOnPremWorkspaces": { + "properties": { + "workspaces": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "workspaces" + ], + "type": "object" + }, + "ToscaWorkspaces": { + "properties": { + "kind": { + "enum": [ + "tosca-onprem", + "tosca-cloud" + ], + "type": "string" + }, + "workspaces": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "workspaces", + "kind" + ], + "type": "object" + }, + "UploadResult": { + "properties": { + "downloadUrl": { + "description": "Short-lived download URL", + "type": "string" + }, + "fileName": { + "description": "The storage key, not the original filename", + "type": "string" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "success", + "downloadUrl", + "fileName" + ], + "type": "object" + }, + "UploadTextResult": { + "properties": { + "chatAssetId": { + "description": "Only when a chatId was supplied and the asset was recorded", + "type": "string" + }, + "fileName": { + "description": "The storage key, not the original filename", + "type": "string" + }, + "success": { + "const": true, + "type": "boolean" + }, + "url": { + "description": "Presigned download URL", + "type": "string" + } + }, + "required": [ + "success", + "url", + "fileName" + ], + "type": "object" + }, + "User": { + "properties": { + "autoSave": { + "type": [ + "boolean", + "null" + ] + }, + "avatar": { + "type": [ + "string", + "null" + ] + }, + "createdAt": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "creationPurpose": { + "type": "string" + }, + "email": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "lastLogin": { + "description": "ISO-8601 timestamp", + "type": "string" + }, + "lastName": { + "type": "string" + } + }, + "required": [ + "id", + "email", + "firstName", + "lastName", + "avatar", + "autoSave", + "creationPurpose", + "lastLogin", + "createdAt" + ], + "type": "object" + }, + "UserPreferences": { + "properties": { + "theme": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "theme" + ], + "type": "object" + }, + "WorkerWorkspaceInfo": { + "properties": { + "fileCount": { + "type": "integer" + }, + "maxFileSize": { + "description": "Bytes", + "type": "integer" + }, + "paths": { + "properties": { + "outputs": { + "type": "string" + }, + "uploads": { + "type": "string" + } + }, + "required": [ + "uploads", + "outputs" + ], + "type": "object" + }, + "projectId": { + "type": "string" + }, + "scenarioId": { + "type": "string" + }, + "storageLimit": { + "description": "Bytes", + "type": "integer" + }, + "storageUsed": { + "description": "Bytes", + "type": "integer" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "scenarioId", + "teamId", + "projectId", + "storageUsed", + "storageLimit", + "maxFileSize", + "fileCount", + "paths" + ], + "type": "object" + }, + "WorkerWorkspaceListing": { + "properties": { + "files": { + "items": { + "properties": { + "downloadUrl": { + "description": "Freshly signed, 1 hour; the stored URL if re-signing failed", + "type": [ + "string", + "null" + ] + }, + "filename": { + "type": "string" + }, + "folder": { + "description": "\"uploads\" or \"outputs\"", + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "description": "The S3 key", + "type": "string" + }, + "mimeType": { + "type": [ + "string", + "null" + ] + }, + "size": { + "description": "Bytes", + "type": "integer" + } + }, + "required": [ + "id", + "filename", + "key", + "folder", + "size", + "mimeType", + "downloadUrl" + ], + "type": "object" + }, + "type": "array" + }, + "scenarioId": { + "type": "string" + } + }, + "required": [ + "scenarioId", + "files" + ], + "type": "object" + }, + "WorkerWorkspaceSyncResult": { + "properties": { + "results": { + "properties": { + "errors": { + "description": "One line per file that could not be synced", + "items": { + "type": "string" + }, + "type": "array" + }, + "synced": { + "type": "integer" + } + }, + "required": [ + "synced", + "errors" + ], + "type": "object" + }, + "success": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "success", + "results" + ], + "type": "object" + }, + "WorkerWorkspaceUploadResult": { + "properties": { + "file": { + "properties": { + "filename": { + "type": "string" + }, + "id": { + "type": "string" + }, + "key": { + "type": "string" + }, + "size": { + "description": "Bytes", + "type": "integer" + } + }, + "required": [ + "id", + "filename", + "key", + "size" + ], + "type": "object" + }, + "success": { + "const": true, + "type": "boolean" + } + }, + "required": [ + "success", + "file" + ], + "type": "object" + }, + "WorkspaceListing": { + "properties": { + "files": { + "items": { + "$ref": "#/components/schemas/ScenarioFile" + }, + "type": "array" + }, + "storageLimit": { + "description": "Bytes", + "type": "integer" + }, + "storageUsed": { + "description": "Bytes", + "type": "integer" + } + }, + "required": [ + "files", + "storageUsed", + "storageLimit" + ], + "type": "object" + }, + "WorkspaceStorage": { + "properties": { + "storageLimit": { + "description": "Bytes", + "type": "integer" + }, + "storageUsed": { + "description": "Bytes", + "type": "integer" + }, + "storageUsedPercent": { + "description": "Rounded percentage", + "type": "integer" + } + }, + "required": [ + "storageUsed", + "storageLimit", + "storageUsedPercent" + ], + "type": "object" + }, + "WorkspaceUploadResult": { + "properties": { + "file": { + "$ref": "#/components/schemas/ScenarioFile" + }, + "success": { + "type": "boolean" + } + }, + "required": [ + "success", + "file" + ], + "type": "object" + } + } + }, + "info": { + "description": "API for Automators DataMaker", + "title": "DataMaker API", + "version": "0.9.17" + }, + "openapi": "3.1.0", + "paths": { + "/address/availability": { + "get": { + "description": "Whether the address dataset is configured and available", + "operationId": "getAddressAvailability", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddressAvailability" + } + } + }, + "description": "Whether the address dataset is configured and queryable" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Address" + ] + } + }, + "/address/cities": { + "get": { + "description": "List distinct cities for a country (code or name)", + "operationId": "getAddressCities", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StringList" + } + } + }, + "description": "Distinct cities for the country and optional region. Empty array when unavailable" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Address" + ] + } + }, + "/address/countries": { + "get": { + "description": "List distinct countries available in the address dataset", + "operationId": "getAddressCountries", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StringList" + } + } + }, + "description": "Distinct countries in the dataset. Empty array when the dataset is unavailable, never an error" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Address" + ] + } + }, + "/address/postcodes": { + "get": { + "description": "List distinct postcodes for a country (code or name)", + "operationId": "getAddressPostcodes", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StringList" + } + } + }, + "description": "Distinct postcodes for the country and optional region. Empty array when unavailable" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Address" + ] + } + }, + "/address/regions": { + "get": { + "description": "List distinct regions for a country (code or name)", + "operationId": "getAddressRegions", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StringList" + } + } + }, + "description": "Distinct regions for the country. Empty array when unavailable" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Address" + ] + } + }, + "/agent/approvals": { + "get": { + "description": "List pending approval requests for a chat", + "operationId": "getAgentApprovals", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ApprovalDecision" + }, + "type": "array" + } + } + }, + "description": "Pending approval requests for the chat; empty when none" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Approvals" + ] + }, + "post": { + "description": "Resolve or create an approval request for a large agent write", + "operationId": "postAgentApprovals", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "chatId": { + "type": "string" + }, + "rowCount": { + "type": "number" + }, + "targetKind": { + "type": "string" + }, + "targetRef": { + "type": "string" + }, + "tool": { + "type": "string" + } + }, + "required": [ + "tool", + "rowCount" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApprovalDecision" + } + } + }, + "description": "Whether the write may proceed, or the pending request blocking it" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Approvals" + ] + } + }, + "/agent/approvals/{id}/deny": { + "post": { + "description": "Reject a large agent write", + "operationId": "postAgentApprovalsByIdDeny", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "description": "The request after denying" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No pending approval with that id" + } + }, + "tags": [ + "Approvals" + ] + } + }, + "/agent/approvals/{id}/grant": { + "post": { + "description": "Approve a large agent write", + "operationId": "postAgentApprovalsByIdGrant", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApprovalStatus" + } + } + }, + "description": "The request after granting" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No pending approval with that id" + } + }, + "tags": [ + "Approvals" + ] + } + }, + "/analyze-image": { + "post": { + "description": "Analyze image and return description", + "operationId": "postAnalyze-image", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageAnalysisResult" + } + } + }, + "description": "A model-generated description of the image" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No image URL in the body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The model returned nothing, or the analysis threw" + } + }, + "tags": [ + "Analyze" + ] + } + }, + "/analyze-pdf": { + "post": { + "description": "Analyze PDF documents by extracting structured key information like dates, titles, invoice numbers, and other business document fields", + "operationId": "postAnalyze-pdf", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PdfAnalysisResult" + } + } + }, + "description": "The extracted fields, plus which of the three extraction paths produced them" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No file in the form data" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Every extraction path failed, or the request threw" + } + }, + "tags": [ + "PDF Analysis" + ] + } + }, + "/apiKeys": { + "get": { + "description": "Get API keys filtered by scope", + "operationId": "getApiKeys", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ApiKey" + }, + "type": "array" + } + } + }, + "description": "API keys in the requested scope" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The scope needs a team or project id" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Project not found" + } + }, + "tags": [ + "API Keys" + ] + }, + "post": { + "description": "Create a new API key", + "operationId": "postApiKeys", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiKey" + } + } + }, + "description": "The created key, including its secret value" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Invalid payload" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "API Keys" + ] + } + }, + "/apiKeys/{id}": { + "delete": { + "description": "Delete an API key", + "operationId": "deleteApiKeysById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The key was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Scenario keys cannot be deleted here" + } + }, + "tags": [ + "API Keys" + ] + }, + "put": { + "description": "Update an API key", + "operationId": "putApiKeysById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiKey" + } + } + }, + "description": "The updated key" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Invalid payload" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "API Keys" + ] + } + }, + "/audit/events": { + "get": { + "description": "List recent agent audit events for the current team", + "operationId": "getAuditEvents", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/AuditEvent" + }, + "type": "array" + } + } + }, + "description": "Recent agent audit events for the team, empty when none" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Audit" + ] + }, + "post": { + "description": "Record an agent tool-invocation audit event", + "operationId": "postAuditEvents", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "category": { + "type": "string" + }, + "chatId": { + "type": "string" + }, + "decision": { + "type": "string" + }, + "durationMs": { + "type": "number" + }, + "error": { + "type": "string" + }, + "finishedAt": { + "type": "string" + }, + "inputDigest": {}, + "sdkSessionId": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string" + }, + "targetKind": { + "type": "string" + }, + "targetRef": { + "type": "string" + }, + "tool": { + "type": "string" + }, + "trackingId": { + "type": "string" + } + }, + "required": [ + "tool" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditEventRef" + } + } + }, + "description": "The recorded event's id" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Audit" + ] + } + }, + "/auth/logout": { + "post": { + "description": "Revoke a refresh token and its rotation family.", + "operationId": "postAuthLogout", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LogoutResult" + } + } + }, + "description": "The token and its whole rotation family were revoked. Answers ok for an unknown token too" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/refresh": { + "post": { + "description": "Rotate a refresh token for a new access token (+ rotated refresh token).", + "operationId": "postAuthRefresh", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionTokens" + } + } + }, + "description": "A new access token and a ROTATED refresh token - the submitted one is now spent" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No refreshToken in the body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The token is unknown, expired, or already spent (reuse revokes the whole family)" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Auth" + ] + } + }, + "/auth/session": { + "post": { + "description": "Exchange the current login for a desktop access + refresh token pair (stay-signed-in).", + "operationId": "postAuthSession", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionTokens" + } + } + }, + "description": "A fresh access + refresh pair for the authenticated caller" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Auth" + ] + } + }, + "/blob/{key}": { + "get": { + "description": "Serve a blob's bytes from the local filesystem store (local-first mode).", + "operationId": "getBlobByKey", + "parameters": [ + { + "in": "path", + "name": "key", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/octet-stream": { + "schema": { + "format": "binary", + "type": "string" + } + } + }, + "description": "The blob's bytes, served under the stored object's own Content-Type" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No blob key in the path" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No blob under that key, or the deployment is not in filesystem-blob mode" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The blob could not be read from disk" + } + }, + "tags": [ + "Blob" + ] + } + }, + "/chat-assets": { + "get": { + "description": "List assets for a chat", + "operationId": "getChat-assets", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChatAsset" + }, + "type": "array" + } + } + }, + "description": "Assets for the chat, each with a freshly signed download URL" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Chat Assets" + ] + }, + "post": { + "description": "Create a new chat asset", + "operationId": "postChat-assets", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "chatId": { + "type": "string" + }, + "filename": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "s3Key": { + "type": "string" + }, + "size": { + "type": "number" + }, + "source": { + "enum": [ + "agent", + "upload" + ], + "type": "string" + } + }, + "required": [ + "chatId", + "filename", + "s3Key" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatAsset" + } + } + }, + "description": "The asset, created or already present" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Chat Assets" + ] + } + }, + "/chat-assets/{id}": { + "delete": { + "description": "Delete a chat asset", + "operationId": "deleteChat-assetsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The asset was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Chat Assets" + ] + } + }, + "/chats": { + "get": { + "description": "Get all chats for the current user", + "operationId": "getChats", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Chat" + }, + "type": "array" + } + } + }, + "description": "Chats the caller can see in scope" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Chats" + ] + }, + "post": { + "description": "Create a new chat", + "operationId": "postChats", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "type": "string" + }, + "messages": {}, + "title": { + "default": "New Chat", + "type": "string" + } + }, + "required": [ + "id" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Chat" + } + } + }, + "description": "The created chat" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the project's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Project not found" + } + }, + "tags": [ + "Chats" + ] + } + }, + "/chats/{id}": { + "delete": { + "description": "Delete a chat", + "operationId": "deleteChatsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The chat was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No chat with that id" + } + }, + "tags": [ + "Chats" + ] + }, + "get": { + "description": "Get a chat by id", + "operationId": "getChatsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Chat" + } + } + }, + "description": "The chat, including its full transcript" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No chat with that id" + } + }, + "tags": [ + "Chats" + ] + }, + "put": { + "description": "Update a chat", + "operationId": "putChatsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "messages": {}, + "title": { + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Chat" + } + } + }, + "description": "The updated chat" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No chat with that id" + } + }, + "tags": [ + "Chats" + ] + } + }, + "/config": { + "get": { + "description": "Get shared datamaker config", + "operationId": "getConfig", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatamakerConfig" + } + } + }, + "description": "Server-owned limits and the AI/agent endpoints a desktop should route through" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Config" + ] + } + }, + "/connections": { + "get": { + "description": "Get all connections", + "operationId": "getConnections", + "parameters": [], + "responses": {}, + "tags": [ + "Connections" + ] + }, + "post": { + "description": "Create a new connection", + "operationId": "postConnections", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "connectionString": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + }, + "teamId": { + "type": "string" + }, + "type": { + "enum": [ + "db2", + "postgresql", + "mysql", + "mssql", + "mongodb", + "oracle" + ], + "type": "string" + } + }, + "required": [ + "name", + "type", + "connectionString", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Connections" + ] + } + }, + "/connections/tables": { + "get": { + "description": "Get tables and metadata for a specific connection", + "operationId": "getConnectionsTables", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ConnectionTable" + }, + "type": "array" + } + } + }, + "description": "Every table in the public schema with its columns, row count and inbound foreign keys" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No connection with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The query failed, or the database type is not supported" + } + }, + "tags": [ + "Tables" + ] + } + }, + "/connections/test": { + "post": { + "description": "Test a connection", + "operationId": "postConnectionsTest", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "connectionString": { + "type": "string" + }, + "type": { + "enum": [ + "db2", + "postgresql", + "mysql", + "mssql", + "mongodb", + "oracle" + ], + "type": "string" + } + }, + "required": [ + "connectionString", + "type" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Connections" + ] + } + }, + "/connections/{id}": { + "delete": { + "description": "Delete a connection", + "operationId": "deleteConnectionsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Connections" + ] + }, + "get": { + "description": "Get a single connection by id", + "operationId": "getConnectionsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Connections" + ] + }, + "put": { + "description": "Update a connection", + "operationId": "putConnectionsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "connectionString": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + }, + "teamId": { + "type": "string" + }, + "type": { + "enum": [ + "db2", + "postgresql", + "mysql", + "mssql", + "mongodb", + "oracle" + ], + "type": "string" + } + }, + "required": [ + "name", + "type", + "connectionString", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Connections" + ] + } + }, + "/customDataTypes": { + "get": { + "description": "Get all custom data types", + "operationId": "getCustomDataTypes", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/CustomDataType" + }, + "type": "array" + } + } + }, + "description": "Custom data types in scope" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Custom Data Types" + ] + }, + "post": { + "description": "Create a new custom data type", + "operationId": "postCustomDataTypes", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdBy": { + "type": "string" + }, + "fieldConfig": {}, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "teamId", + "projectId", + "createdBy" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomDataType" + } + } + }, + "description": "The created custom data type" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Custom Data Types" + ] + } + }, + "/customDataTypes/{id}": { + "delete": { + "description": "Delete a custom data type", + "operationId": "deleteCustomDataTypesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The custom data type was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Custom Data Types" + ] + }, + "put": { + "description": "Update a custom data type", + "operationId": "putCustomDataTypesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdBy": { + "type": "string" + }, + "fieldConfig": {}, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "teamId", + "projectId", + "createdBy" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomDataType" + } + } + }, + "description": "The updated custom data type" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Custom Data Types" + ] + } + }, + "/datamaker": { + "post": { + "description": "Generate data based on template fields", + "operationId": "postDatamaker", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneratedData" + } + } + }, + "description": "The generated rows plus the reference data they were built from" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No fields in the body, or a quantity outside the configured limit" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No project with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Generation failed" + } + }, + "tags": [ + "DataMaker" + ] + } + }, + "/endpointFolders": { + "get": { + "description": "Route to get all endpoint folders", + "operationId": "getEndpointFolders", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/EndpointFolder" + }, + "type": "array" + } + } + }, + "description": "Endpoint folders in scope" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoint Folders" + ] + }, + "post": { + "description": "Route to create a new endpoint folder", + "operationId": "postEndpointFolders", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdAt": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EndpointFolder" + } + } + }, + "description": "The created folder" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The folder name is already in use" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoint Folders" + ] + } + }, + "/endpointFolders/{id}": { + "delete": { + "description": "Route to delete an endpoint folder", + "operationId": "deleteEndpointFoldersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EndpointFolder" + } + } + }, + "description": "The deleted folder is returned, not a message" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoint Folders" + ] + }, + "put": { + "description": "Route to update an endpoint folder", + "operationId": "putEndpointFoldersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EndpointFolder" + } + } + }, + "description": "The updated folder" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The folder name is already in use" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoint Folders" + ] + } + }, + "/endpoints": { + "get": { + "description": "Get all endpoints", + "operationId": "getEndpoints", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Endpoint" + }, + "type": "array" + } + } + }, + "description": "Endpoints in scope. Credentials are masked unless the caller used a scenario execution key" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoints" + ] + }, + "post": { + "description": "Create a new endpoint", + "operationId": "postEndpoints", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdBy": { + "type": "string" + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "headers": {}, + "id": { + "type": "string" + }, + "integrationId": { + "type": [ + "string", + "null" + ] + }, + "meta": {}, + "method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "queryParams": {}, + "teamId": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "method", + "url", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Endpoint" + } + } + }, + "description": "The created endpoint, credentials masked" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The referenced integration is not usable" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoints" + ] + } + }, + "/endpoints/auth-resolve": { + "post": { + "description": "Resolve an endpoint's usable credentials: the Authorization header (decrypt Basic / exchange OAuth2), the auth type, and - for Basic auth - the decrypted username/password. Used by the desktop execution sidecar and by scenarios that need to authenticate to an external system with the endpoint's real credentials. Decryption stays central; access is gated by the same endpoint access check as the rest of the API.", + "operationId": "postEndpointsAuth-resolve", + "parameters": [], + "responses": {}, + "tags": [ + "Endpoints" + ] + } + }, + "/endpoints/{id}": { + "delete": { + "description": "Delete an endpoint", + "operationId": "deleteEndpointsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The endpoint was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoints" + ] + }, + "get": { + "description": "Get an endpoint by ID", + "operationId": "getEndpointsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Endpoint" + } + } + }, + "description": "The endpoint. Credentials are masked unless the caller used a scenario execution key" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope" + } + }, + "tags": [ + "Endpoints" + ] + }, + "put": { + "description": "Update an endpoint", + "operationId": "putEndpointsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdBy": { + "type": "string" + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "headers": {}, + "id": { + "type": "string" + }, + "integrationId": { + "type": [ + "string", + "null" + ] + }, + "meta": {}, + "method": { + "enum": [ + "GET", + "POST", + "PUT", + "PATCH", + "DELETE" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "queryParams": {}, + "teamId": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "method", + "url", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Endpoint" + } + } + }, + "description": "The updated endpoint, credentials masked" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The referenced integration is not usable" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Endpoints" + ] + } + }, + "/execute-python": { + "post": { + "description": "Execute a Python file from a URL using the datamaker runner. By default waits for completion, set async=true for immediate return.", + "operationId": "postExecute-python", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "async": { + "default": false, + "type": "boolean" + }, + "projectId": { + "type": "string" + }, + "requirementsUrl": { + "format": "uri", + "type": "string" + }, + "url": { + "format": "uri", + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PythonExecutionResult" + } + } + }, + "description": "The run. `async=true` answers as soon as the job is queued, carrying only `jobId`, `status` and `message`" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PythonExecutionError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PythonExecutionError" + } + } + }, + "description": "The script or requirements URL is not reachable from this project" + }, + "408": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PythonExecutionError" + } + } + }, + "description": "The run did not finish inside the poll window" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PythonExecutionError" + } + } + }, + "description": "The run failed" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PythonExecutionError" + } + } + }, + "description": "The local runner is unreachable" + } + }, + "tags": [ + "Execution" + ] + } + }, + "/export/db": { + "post": { + "description": "Export to Database", + "operationId": "postExportDb", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatabaseExportResult" + } + } + }, + "description": "The driver's query result, returned verbatim" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The connection is marked read-only, or the caller lacks the permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No connection with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The query failed, or the database type is not supported" + } + }, + "tags": [ + "Export" + ] + } + }, + "/export/rest": { + "post": { + "description": "Export to REST API", + "operationId": "postExportRest", + "parameters": [], + "responses": {}, + "tags": [ + "Export" + ] + } + }, + "/feedback": { + "get": { + "description": "Get all feedback", + "operationId": "getFeedback", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Feedback" + }, + "type": "array" + } + } + }, + "description": "All feedback" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Feedback" + ] + }, + "post": { + "description": "Create new feedback", + "operationId": "postFeedback", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "comment": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "feeling": { + "enum": [ + "EXCITED", + "HAPPY", + "SAD", + "HATE" + ], + "type": "string" + }, + "id": { + "type": "string" + } + }, + "required": [ + "feeling", + "createdBy" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Feedback" + } + } + }, + "description": "The created feedback" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Feedback" + ] + } + }, + "/feedback/{id}": { + "delete": { + "description": "Delete feedback", + "operationId": "deleteFeedbackById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The feedback was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No feedback with that id" + } + }, + "tags": [ + "Feedback" + ] + }, + "put": { + "description": "Update feedback", + "operationId": "putFeedbackById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "comment": { + "type": "string" + }, + "createdBy": { + "type": "string" + }, + "feeling": { + "enum": [ + "EXCITED", + "HAPPY", + "SAD", + "HATE" + ], + "type": "string" + }, + "id": { + "type": "string" + } + }, + "required": [ + "feeling", + "createdBy" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Feedback" + } + } + }, + "description": "The updated feedback" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No feedback with that id" + } + }, + "tags": [ + "Feedback" + ] + } + }, + "/fields": { + "get": { + "description": "Get all fields", + "operationId": "getFields", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FieldType" + }, + "type": "array" + } + } + }, + "description": "Every generator type with its options and display metadata" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Fields" + ] + } + }, + "/generate/database-templates": { + "post": { + "description": "Analyze a relational database and propose DataMaker templates + prompts", + "operationId": "postGenerateDatabase-templates", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DatabaseTemplatesProposal" + } + } + }, + "description": "One proposed template per table, plus the dependency-safe population order" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No database connection in the body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No connection with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The database could not be analyzed" + } + }, + "tags": [ + "DataMaker" + ] + } + }, + "/generate/openapi/{format}": { + "post": { + "description": "Generate an OpenAPI spec from a JSON or YAML file.", + "operationId": "postGenerateOpenapiByFormat", + "parameters": [ + { + "in": "path", + "name": "format", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "A stream of concatenated JSON template objects, one per operation in the spec" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "A format other than json or yaml, or a body that is not a parseable OpenAPI spec" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "Generation threw; the body is the raw error text" + } + }, + "tags": [ + "OpenAPI" + ] + } + }, + "/generate/sensitive": { + "post": { + "description": "Classify template fields as sensitive (PII) data", + "operationId": "postGenerateSensitive", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ClassifiedFields" + } + } + }, + "description": "The submitted fields echoed back with `sensitive` set. A field whose classification failed comes back unchanged rather than failing the batch" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The body is not an array of fields" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Classification failed outright" + } + }, + "tags": [ + "DataMaker" + ] + } + }, + "/generate/template": { + "post": { + "description": "Generate a template from JSON or CSV data.", + "operationId": "postGenerateTemplate", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GeneratedTemplateFields" + } + } + }, + "description": "A bare ARRAY of generated fields - not a template object wrapping them" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The feature is not enabled for this deployment" + }, + "405": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Method not allowed" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The template could not be generated" + } + }, + "tags": [ + "Templates" + ] + } + }, + "/getcsrftoken": { + "post": { + "description": "Get CSRF token from SAP system", + "operationId": "postGetcsrftoken", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "authorization": {}, + "endpointId": { + "minLength": 1, + "type": "string" + }, + "sapUrl": { + "format": "uri", + "type": "string" + } + }, + "required": [ + "sapUrl" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsrfToken" + } + } + }, + "description": "The token and the session cookie it is bound to - SAP rejects the token without the cookie" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Fetching the token from the SAP system failed" + } + }, + "tags": [ + "CSRF" + ] + } + }, + "/integrations": { + "get": { + "description": "Get all integrations", + "operationId": "getIntegrations", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Integration" + }, + "type": "array" + } + } + }, + "description": "Integrations in scope, credentials masked" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Integrations" + ] + }, + "post": { + "description": "Create a new integration", + "operationId": "postIntegrations", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "auth": {}, + "createdBy": { + "type": "string" + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "headers": {}, + "id": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "scope": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "origin", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Integration" + } + } + }, + "description": "The created integration, credentials masked" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/test": { + "post": { + "description": "Probe an integration's connection (no persistence). Returns whether the origin + credentials are reachable and accepted.", + "operationId": "postIntegrationsTest", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "auth": {}, + "id": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "origin": { + "type": "string" + } + }, + "required": [ + "origin" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConnectionVerdict" + } + } + }, + "description": "Probe verdict. Answers 200 with ok:false for a reachable system that rejected the credentials, so callers read `ok`, not the status" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Origin missing or not a valid URL" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}": { + "delete": { + "description": "Delete an integration", + "operationId": "deleteIntegrationsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The integration was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No integration with that id in the caller's scope" + } + }, + "tags": [ + "Integrations" + ] + }, + "get": { + "description": "Get an integration by ID", + "operationId": "getIntegrationsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Integration" + } + } + }, + "description": "The integration, credentials masked" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No integration with that id in the caller's scope" + } + }, + "tags": [ + "Integrations" + ] + }, + "put": { + "description": "Update an integration", + "operationId": "putIntegrationsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "auth": {}, + "createdBy": { + "type": "string" + }, + "endpointFolderId": { + "type": [ + "string", + "null" + ] + }, + "headers": {}, + "id": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "name": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "scope": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "origin", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Integration" + } + } + }, + "description": "The updated integration, credentials masked" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No integration with that id in the caller's scope" + } + }, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/endpoints": { + "post": { + "description": "Create a DataMaker endpoint for one of this integration's OData services, linked to the integration so it INHERITS the system credentials (no auth is stored on the endpoint itself). The service root URL is resolved from the Gateway catalog by service id. Use this instead of creating a raw endpoint - a standalone endpoint has no credentials and SAP will reject it (401).", + "operationId": "postIntegrationsByIdEndpoints", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "entitySet": { + "type": "string" + }, + "name": { + "type": "string" + }, + "service": { + "type": "string" + } + }, + "required": [ + "service" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/createmeta": { + "get": { + "description": "Create-metadata for a project (?project=KEY required): without ?issueTypeId= lists the project's issue types; with it, that type's fields incl. which are required and their allowed values. Call before creating an issue so the payload is valid.", + "operationId": "getIntegrationsByIdJiraCreatemeta", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/issues": { + "post": { + "description": "Create a Jira issue (e.g. a defect from a failed scenario run). Body: projectKey, issueType (name, e.g. Bug), summary, optional description (plain text - converted to ADF), labels, priority. Returns the new key + browse URL. Blocked when the connection is read-only.", + "operationId": "postIntegrationsByIdJiraIssues", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "description": { + "type": "string" + }, + "issueType": { + "minLength": 1, + "type": "string" + }, + "labels": { + "items": { + "type": "string" + }, + "type": "array" + }, + "priority": { + "type": "string" + }, + "projectKey": { + "minLength": 1, + "type": "string" + }, + "summary": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "projectKey", + "issueType", + "summary" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/issues/{issueKey}": { + "get": { + "description": "Read one Jira issue by key (e.g. PROJ-123): summary, status, description flattened to plain text, labels, links, parent. The richest input for deriving test data from a story's acceptance criteria.", + "operationId": "getIntegrationsByIdJiraIssuesByIssueKey", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "issueKey", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/issues/{issueKey}/comment": { + "post": { + "description": "Add a plain-text comment to a Jira issue (converted to ADF). Blocked when the connection is read-only.", + "operationId": "postIntegrationsByIdJiraIssuesByIssueKeyComment", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "issueKey", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "text": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "text" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/issues/{issueKey}/transitions": { + "get": { + "description": "List the workflow transitions currently available on a Jira issue (id + name + target status). Read-only; use the POST variant to apply one.", + "operationId": "getIntegrationsByIdJiraIssuesByIssueKeyTransitions", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "issueKey", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + }, + "post": { + "description": "Apply a workflow transition to a Jira issue (transitionId from the GET variant). Blocked when the connection is read-only.", + "operationId": "postIntegrationsByIdJiraIssuesByIssueKeyTransitions", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "issueKey", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "transitionId": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "transitionId" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/projects": { + "get": { + "description": "List the Jira projects visible to this integration's credentials. Query: ?q= filters by name/key; ?limit= caps the count. Returns id, key (use in JQL and issue creation), name, type.", + "operationId": "getIntegrationsByIdJiraProjects", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/jira/search": { + "get": { + "description": "Search Jira issues with JQL (?jql= required, ?limit= caps the count, default 25). Returns compact issues (key, summary, status, type, priority, assignee, url) plus nextPageToken when more pages exist.", + "operationId": "getIntegrationsByIdJiraSearch", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/services": { + "get": { + "description": "List the OData services registered on the SAP system this integration points at (Gateway CATALOGSERVICE), using the integration's stored credentials. Lets the agent discover what a connected system exposes before any per-service endpoint exists. Query: ?q= filters by id/description; ?status=active|inactive live-probes reachability; ?limit= caps the count.", + "operationId": "getIntegrationsByIdServices", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/onprem/checkin-all": { + "post": { + "description": "Repository-level check-in: commit ALL checked-out objects in a Tosca On-Prem workspace in one call (the workspace-level CheckInAll task - GET {ws}/task/CheckInAll). Individual writes already self-commit; use this to flush changes deliberately left checked out, or a workspace showing pending check-outs. Body: { workspace, comment? }.", + "operationId": "postIntegrationsByIdToscaOnpremCheckin-all", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "comment": { + "type": "string" + }, + "workspace": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "workspace" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/onprem/execution-entries": { + "post": { + "description": "Add a test case to a Tosca On-Prem execution list (the CreateExecutionEntry 'drop' task), checked out -> created -> checked in. Create the execution list itself via POST .../tosca/onprem/objects with objType 'ExecutionList'. NOTE: classic Tosca does not RUN tests via REST (a Tosca agent/DEX does); read verdicts back via GET .../objects/:id. Body: { workspace, executionListId, testCaseId, comment? }.", + "operationId": "postIntegrationsByIdToscaOnpremExecution-entries", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "comment": { + "type": "string" + }, + "executionListId": { + "minLength": 1, + "type": "string" + }, + "testCaseId": { + "minLength": 1, + "type": "string" + }, + "workspace": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "workspace", + "executionListId", + "testCaseId" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/onprem/objects": { + "post": { + "description": "Create a child object (TestSheet, Instance, Attribute, ExecutionList, TestCase, Folder, ...) under a parent on a Tosca On-Prem system, optionally setting attributes. The parent is checked out, the object created (+ attributes set), then checked in. Body: { workspace, parentId, objType, attributes?, comment? }.", + "operationId": "postIntegrationsByIdToscaOnpremObjects", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "attributes": { + "items": { + "properties": { + "Name": { + "type": "string" + }, + "Value": { + "type": "string" + } + }, + "required": [ + "Name", + "Value" + ], + "type": "object" + }, + "type": "array" + }, + "comment": { + "type": "string" + }, + "objType": { + "minLength": 1, + "type": "string" + }, + "parentId": { + "minLength": 1, + "type": "string" + }, + "workspace": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "workspace", + "parentId", + "objType" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/onprem/objects/{objectId}": { + "get": { + "description": "Read a Tosca On-Prem object's attribute map. Use to inspect EXECUTION RESULTS (Result / ActualLog / ExecutionResult) after a run. Query: ?workspace= (required).", + "operationId": "getIntegrationsByIdToscaOnpremObjectsByObjectId", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "objectId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/onprem/query": { + "post": { + "description": "Run a TQL search against a Tosca On-Prem system and return the matching object ids. Scoped to ?from= object id when given, else the project root. Body: { workspace, tql, from? }.", + "operationId": "postIntegrationsByIdToscaOnpremQuery", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "from": { + "type": "string" + }, + "tql": { + "minLength": 1, + "type": "string" + }, + "workspace": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "workspace", + "tql" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/onprem/workspaces": { + "get": { + "description": "List the workspaces on a connected Tosca On-Prem system. A workspace name is needed by the other on-prem write routes.", + "operationId": "getIntegrationsByIdToscaOnpremWorkspaces", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToscaOnPremWorkspaces" + } + } + }, + "description": "Workspace names on the connected Tosca On-Prem system" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The Tosca system could not be reached" + } + }, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/playlist-runs/{playlistRunId}/testcase-runs": { + "get": { + "description": "List test case runs for a Tosca Cloud playlist run. ?space= overrides the default space; ?limit= caps the count. Returns id, displayName, testCaseId, state.", + "operationId": "getIntegrationsByIdToscaPlaylist-runsByPlaylistRunIdTestcase-runs", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "playlistRunId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/playlists": { + "get": { + "description": "List Tosca Cloud playlists (execution containers). Query: ?q= filters by description (Contains); ?space= overrides the default space; ?limit= caps the count. Returns id, name, description, state.", + "operationId": "getIntegrationsByIdToscaPlaylists", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/playlists/{playlistId}/runs": { + "get": { + "description": "List runs of a Tosca Cloud playlist. ?space= overrides the default space; ?limit= caps the count. Returns id, name, state, createdAt.", + "operationId": "getIntegrationsByIdToscaPlaylistsByPlaylistIdRuns", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "playlistId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/testcase-runs/{testCaseRunId}/steps": { + "get": { + "description": "Read the executed step tree for a Tosca Cloud test case run (pass/fail per step, values, messages). ?space= overrides the default space. Returns an empty steps array when no step log exists (404 from Tosca is normal for older runs).", + "operationId": "getIntegrationsByIdToscaTestcase-runsByTestCaseRunIdSteps", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "testCaseRunId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/testcases": { + "get": { + "description": "List Tosca Cloud test cases for this integration's system. Query: ?q= filters by description (Contains); ?space= overrides the default space; ?limit= caps the count. Returns id (use to read the design), name, description, status.", + "operationId": "getIntegrationsByIdToscaTestcases", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + }, + "post": { + "description": "Create a new Tosca Cloud test case in this integration's space from a design payload (the model-based-test builder shape returned by GET .../tosca/testcases/:id). The typical flow is read a design, modify it to cover the data gaps found, then POST it here. Structural ids are cleared server-side so Tosca mints new ones; module references are preserved. Body: { name, design, space? }.", + "operationId": "postIntegrationsByIdToscaTestcases", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "design": { + "additionalProperties": {}, + "type": "object" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "space": { + "type": "string" + } + }, + "required": [ + "name", + "design" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/testcases/{testCaseId}": { + "get": { + "description": "Read a single Tosca Cloud test case's design (model-based-test builder payload: configuration parameters, test step folders, module/attribute references, value ranges). The richest input for data-gap analysis. ?space= overrides the default space.", + "operationId": "getIntegrationsByIdToscaTestcasesByTestCaseId", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "testCaseId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": {}, + "tags": [ + "Integrations" + ] + } + }, + "/integrations/{id}/tosca/workspaces": { + "get": { + "description": "List the workspaces/spaces available on a connected Tosca system (on-prem returns the server's workspaces; Cloud enumerates the tenant's spaces). Returns { id, name }[]. Used to disambiguate which workspace to list test cases from.", + "operationId": "getIntegrationsByIdToscaWorkspaces", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToscaWorkspaces" + } + } + }, + "description": "Workspaces, plus the kind of Tosca system they came from" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The Tosca system could not be reached" + } + }, + "tags": [ + "Integrations" + ] + } + }, + "/internal/workspace/{scenarioId}/files": { + "get": { + "description": "List workspace files for worker sync", + "operationId": "getInternalWorkspaceByScenarioIdFiles", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerWorkspaceListing" + } + } + }, + "description": "The scenario's workspace files, newest first, with freshly signed download URLs" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Listing the files failed" + } + }, + "tags": [ + "Internal Workspace" + ] + } + }, + "/internal/workspace/{scenarioId}/info": { + "get": { + "description": "Get workspace info for worker", + "operationId": "getInternalWorkspaceByScenarioIdInfo", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerWorkspaceInfo" + } + } + }, + "description": "The scenario's workspace quota, usage and worker-side paths" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Reading the workspace info failed" + } + }, + "tags": [ + "Internal Workspace" + ] + } + }, + "/internal/workspace/{scenarioId}/sync": { + "post": { + "description": "Sync workspace metadata after worker run", + "operationId": "postInternalWorkspaceByScenarioIdSync", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "files": { + "items": { + "properties": { + "action": { + "enum": [ + "created", + "updated", + "deleted" + ], + "type": "string" + }, + "filename": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "number" + } + }, + "required": [ + "filename", + "size", + "action" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "files" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerWorkspaceSyncResult" + } + } + }, + "description": "Metadata synced. PARTIAL FAILURES STILL ANSWER 200 - check `results.errors`" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The sync failed outright" + } + }, + "tags": [ + "Internal Workspace" + ] + } + }, + "/internal/workspace/{scenarioId}/upload": { + "post": { + "description": "Upload output file from worker", + "operationId": "postInternalWorkspaceByScenarioIdUpload", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkerWorkspaceUploadResult" + } + } + }, + "description": "The file was stored and recorded against the scenario" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No file in the form data, a folder other than 'outputs', a file over the size cap, or the workspace quota is exhausted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The upload to blob storage failed" + } + }, + "tags": [ + "Internal Workspace" + ] + } + }, + "/keymaps": { + "get": { + "description": "List key maps for a project: one row per (mapName, object) with entry counts and last update", + "operationId": "getKeymaps", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/KeyMapSummary" + }, + "type": "array" + } + } + }, + "description": "One row per (mapName, object), ordered by both" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "KeyMaps" + ] + } + }, + "/keymaps/entries": { + "post": { + "description": "Batch-upsert key map entries: records old-to-new key mappings on the (projectId, mapName, object, oldKey) unique key. Last write wins for newKey.", + "operationId": "postKeymapsEntries", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "entries": { + "description": "Old-to-new key pairs to upsert", + "items": { + "properties": { + "newKey": { + "description": "The key minted in the target system", + "minLength": 1, + "type": "string" + }, + "oldKey": { + "description": "The key in the source system", + "minLength": 1, + "type": "string" + } + }, + "required": [ + "oldKey", + "newKey" + ], + "type": "object" + }, + "maxItems": 5000, + "minItems": 1, + "type": "array" + }, + "mapName": { + "description": "Logical map name grouping entries, e.g. 'sap-material-migration'", + "minLength": 1, + "type": "string" + }, + "object": { + "description": "The domain object type, e.g. 'Material' or 'BusinessPartner'", + "minLength": 1, + "type": "string" + }, + "projectId": { + "description": "The project this map belongs to (falls back to the key/session scope)", + "type": "string" + }, + "runId": { + "description": "Optional run/source metadata: the scenario run or job that minted the keys", + "type": "string" + } + }, + "required": [ + "mapName", + "object", + "entries" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeyMapUpsertResult" + } + } + }, + "description": "How many rows were inserted or updated" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The entries could not be upserted" + } + }, + "tags": [ + "KeyMaps" + ] + } + }, + "/keymaps/lookup": { + "post": { + "description": "Batch lookup: translate source-system keys to target-system keys. Returns found mappings and the keys that have no mapping yet. POST because oldKeys can be large.", + "operationId": "postKeymapsLookup", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "mapName": { + "description": "The map to look up in", + "minLength": 1, + "type": "string" + }, + "object": { + "description": "The domain object type", + "minLength": 1, + "type": "string" + }, + "oldKeys": { + "description": "Source-system keys to translate", + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 5000, + "minItems": 1, + "type": "array" + }, + "projectId": { + "description": "The project the map belongs to (falls back to the key/session scope)", + "type": "string" + } + }, + "required": [ + "mapName", + "object", + "oldKeys" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeyMapLookupResult" + } + } + }, + "description": "Resolved mappings, plus the keys with no mapping yet" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "KeyMaps" + ] + } + }, + "/keymaps/{mapName}": { + "delete": { + "description": "Drop a key map (all its entries), optionally scoped to one object type via the `object` query param", + "operationId": "deleteKeymapsByMapName", + "parameters": [ + { + "in": "path", + "name": "mapName", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeyMapDeleteResult" + } + } + }, + "description": "The map was dropped, with the number of entries removed" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The key map could not be deleted" + } + }, + "tags": [ + "KeyMaps" + ] + } + }, + "/keymaps/{mapName}/entries": { + "get": { + "description": "Paginated entries of a key map for inspection. Optional `object` filter; `page` is 1-based.", + "operationId": "getKeymapsByMapNameEntries", + "parameters": [ + { + "in": "path", + "name": "mapName", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KeyMapEntriesPage" + } + } + }, + "description": "One page of entries, with the total for the filter" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "KeyMaps" + ] + } + }, + "/licenses": { + "get": { + "description": "Get all licenses activated for a team (keys are masked)", + "operationId": "getLicenses", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/License" + }, + "type": "array" + } + } + }, + "description": "Licenses activated for the team. The key is masked; only `keyMasked` and `keyId` are returned" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Licenses" + ] + } + }, + "/licenses/activate": { + "post": { + "description": "Activate a license key issued by auth.automators.com on this deployment", + "operationId": "postLicensesActivate", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "key": { + "minLength": 1, + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "teamId", + "key" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/License" + } + } + }, + "description": "The activated license, key masked" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The key is invalid, expired, revoked, of the wrong type, or issued for another product" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Licenses" + ] + } + }, + "/licenses/status": { + "get": { + "description": "Effective license status for this deployment and (optionally) a team, including seat usage", + "operationId": "getLicensesStatus", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LicenseStatus" + } + } + }, + "description": "Effective licensing state for this deployment" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Licenses" + ] + } + }, + "/logs": { + "get": { + "description": "Get all scenario execution logs", + "operationId": "getLogs", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ScenarioLog" + }, + "type": "array" + } + } + }, + "description": "Execution logs in scope, log lines normalised" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Scenario Logs" + ] + }, + "post": { + "description": "Create a new scenario execution log entry", + "operationId": "postLogs", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "error": { + "description": "Error message if failed", + "type": "string" + }, + "jobId": { + "description": "The BullMQ job ID", + "type": "string" + }, + "logs": { + "default": [], + "description": "Log entries", + "items": { + "type": "string" + }, + "type": "array" + }, + "output": { + "description": "Final execution output", + "type": "string" + }, + "scenarioId": { + "description": "The scenario ID being executed", + "type": "string" + }, + "status": { + "description": "Current status of the scenario run", + "enum": [ + "queued", + "running", + "completed", + "failed" + ], + "type": "string" + } + }, + "required": [ + "scenarioId", + "jobId", + "status" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioLog" + } + } + }, + "description": "The created log entry" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The creating team member could not be resolved" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The log could not be created" + } + }, + "tags": [ + "Scenario Logs" + ] + } + }, + "/logs/cleanup": { + "delete": { + "description": "Delete scenario logs older than specified days (default 90)", + "operationId": "deleteLogsCleanup", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LogCleanupResult" + } + } + }, + "description": "How many logs the retention sweep removed, and the cutoff it used" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Scenario Logs" + ] + } + }, + "/logs/job/{jobId}": { + "get": { + "description": "Get scenario logs by BullMQ job ID", + "operationId": "getLogsJobByJobId", + "parameters": [ + { + "in": "path", + "name": "jobId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioLog" + } + } + }, + "description": "The execution log for that BullMQ job" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario log with that identifier" + } + }, + "tags": [ + "Scenario Logs" + ] + }, + "patch": { + "description": "Update a scenario log's terminal state (status/output/error)", + "operationId": "patchLogsJobByJobId", + "parameters": [ + { + "in": "path", + "name": "jobId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "completedAt": { + "description": "ISO timestamp the run finished", + "type": [ + "string", + "null" + ] + }, + "error": { + "description": "Error message, if failed", + "type": [ + "string", + "null" + ] + }, + "output": { + "description": "Final run output", + "type": [ + "string", + "null" + ] + }, + "status": { + "description": "Terminal status (completed/failed)", + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioLog" + } + } + }, + "description": "The log with its terminal state applied" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario log for that job" + } + }, + "tags": [ + "Scenario Logs" + ] + } + }, + "/logs/job/{jobId}/append": { + "patch": { + "description": "Append new log entries to an existing scenario log (useful for streaming)", + "operationId": "patchLogsJobByJobIdAppend", + "parameters": [ + { + "in": "path", + "name": "jobId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "logs": { + "description": "New log entries to append", + "items": { + "type": "string" + }, + "type": "array" + }, + "status": { + "description": "Updated status", + "type": "string" + } + }, + "required": [ + "logs" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioLog" + } + } + }, + "description": "The log with the appended lines" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario log for that job" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The lines could not be appended" + } + }, + "tags": [ + "Scenario Logs" + ] + } + }, + "/logs/scenario/{scenarioId}": { + "get": { + "description": "Get all execution logs for a specific scenario", + "operationId": "getLogsScenarioByScenarioId", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ScenarioLog" + }, + "type": "array" + } + } + }, + "description": "Every execution of that scenario" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Scenario Logs" + ] + } + }, + "/logs/{id}": { + "delete": { + "description": "Delete a scenario log entry", + "operationId": "deleteLogsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The log entry was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario log with that id" + } + }, + "tags": [ + "Scenario Logs" + ] + }, + "get": { + "description": "Get a specific scenario log by ID", + "operationId": "getLogsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioLog" + } + } + }, + "description": "The execution log" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario log with that identifier" + } + }, + "tags": [ + "Scenario Logs" + ] + } + }, + "/masking-policies": { + "get": { + "description": "Get all masking policies for the caller's team/project scope", + "operationId": "getMasking-policies", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/MaskingPolicy" + }, + "type": "array" + } + } + }, + "description": "Policies in scope" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Masking policies" + ] + }, + "post": { + "description": "Create a masking policy scoped to the caller's team/project", + "operationId": "postMasking-policies", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "consistent": { + "description": "Default: same source value masks to the same output", + "type": "boolean" + }, + "description": { + "description": "Short description of the policy (e.g. GDPR profile)", + "type": "string" + }, + "fields": { + "description": "Per-field strategy rows: field matcher + strategy + overrides", + "items": { + "properties": { + "consistent": { + "type": "boolean" + }, + "detectedAs": { + "type": "string" + }, + "field": { + "minLength": 1, + "type": "string" + }, + "note": { + "type": "string" + }, + "reversible": { + "type": "boolean" + }, + "strategy": { + "enum": [ + "faker-replace", + "hash", + "tokenize", + "redact", + "preserve-format" + ], + "type": "string" + } + }, + "required": [ + "field", + "strategy" + ], + "type": "object" + }, + "type": "array" + }, + "keyMapName": { + "description": "Key map name (#2787) scoping consistency to one run's key space", + "type": "string" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "projectId": { + "description": "The project ID this policy belongs to", + "type": "string" + }, + "reversible": { + "description": "Default: masked values can be translated back (tokenization)", + "type": "boolean" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaskingPolicy" + } + } + }, + "description": "The created policy" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The policy could not be created" + } + }, + "tags": [ + "Masking policies" + ] + } + }, + "/masking-policies/{id}": { + "delete": { + "description": "Delete a masking policy", + "operationId": "deleteMasking-policiesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The policy was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No policy with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The policy could not be deleted" + } + }, + "tags": [ + "Masking policies" + ] + }, + "get": { + "description": "Get a masking policy by id", + "operationId": "getMasking-policiesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaskingPolicy" + } + } + }, + "description": "The policy" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No policy with that id in the caller's scope" + } + }, + "tags": [ + "Masking policies" + ] + }, + "patch": { + "description": "Update a masking policy", + "operationId": "patchMasking-policiesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "consistent": { + "type": "boolean" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "fields": { + "items": { + "properties": { + "consistent": { + "type": "boolean" + }, + "detectedAs": { + "type": "string" + }, + "field": { + "minLength": 1, + "type": "string" + }, + "note": { + "type": "string" + }, + "reversible": { + "type": "boolean" + }, + "strategy": { + "enum": [ + "faker-replace", + "hash", + "tokenize", + "redact", + "preserve-format" + ], + "type": "string" + } + }, + "required": [ + "field", + "strategy" + ], + "type": "object" + }, + "type": "array" + }, + "keyMapName": { + "type": [ + "string", + "null" + ] + }, + "name": { + "minLength": 1, + "type": "string" + }, + "reversible": { + "type": "boolean" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaskingPolicy" + } + } + }, + "description": "The updated policy" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No policy with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The policy could not be updated" + } + }, + "tags": [ + "Masking policies" + ] + } + }, + "/packs/catalog": { + "get": { + "description": "Proxy the Hub catalog index (static file on the downloads host). Returns { enabled: false } when HUB_CATALOG_URL is not configured (air-gapped deployments).", + "operationId": "getPacksCatalog", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackCatalog" + } + } + }, + "description": "The catalog index, or `{ enabled: false, packs: [] }` when HUB_CATALOG_URL is unset" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The catalog host is unreachable or answered non-2xx (that body also carries `enabled: true`)" + } + }, + "tags": [ + "Packs" + ] + } + }, + "/packs/catalog/install": { + "post": { + "description": "Fetch a pack from the catalog and install it. Catalog installs REQUIRE a valid ed25519 signature from a pinned publisher key.", + "operationId": "postPacksCatalogInstall", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "confirm": { + "default": false, + "type": "boolean" + }, + "dryRun": { + "default": false, + "type": "boolean" + }, + "name": { + "pattern": "^[a-z0-9][a-z0-9-]*\\/[a-z0-9][a-z0-9-]*$", + "type": "string" + }, + "projectId": { + "type": "string" + } + }, + "required": [ + "projectId", + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackInstallResult" + } + } + }, + "description": "The diff alone on a dry run; the diff plus `created` and `install` when `confirm: true` wrote" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "`confirm: true` was not sent, so nothing was written (that body also carries `diff`)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The Hub catalog is not configured, or no project with that id in the caller's scope" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The pack does not carry a valid signature; catalog installs require one" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The pack could not be fetched from the catalog, or it served an invalid pack" + } + }, + "tags": [ + "Packs" + ] + } + }, + "/packs/export": { + "post": { + "description": "Build a .dmpack.json from selected assets. Strips ids, team/project bindings and endpoint references so the pack is portable; returns the pack with its checksum (unsigned - signing happens at publish time).", + "operationId": "postPacksExport", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "datatypeIds": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "manifest": { + "properties": { + "description": { + "minLength": 1, + "type": "string" + }, + "license": { + "type": "string" + }, + "name": { + "pattern": "^[a-z0-9][a-z0-9-]*\\/[a-z0-9][a-z0-9-]*$", + "type": "string" + }, + "publisher": { + "minLength": 1, + "type": "string" + }, + "requires": { + "default": [], + "items": { + "properties": { + "kind": { + "const": "integration", + "type": "string" + }, + "note": { + "type": "string" + }, + "type": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "kind", + "type" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(-[0-9A-Za-z.-]+)?$", + "type": "string" + } + }, + "required": [ + "name", + "version", + "description", + "publisher" + ], + "type": "object" + }, + "planIds": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "projectId": { + "type": "string" + }, + "scenarioIds": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "skillIds": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "templateIds": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "projectId", + "manifest" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackExportResult" + } + } + }, + "description": "The portable pack with its checksum, plus one warning per asset that could not be included" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No project with that id in the caller's scope" + } + }, + "tags": [ + "Packs" + ] + } + }, + "/packs/import": { + "post": { + "description": "Import a .dmpack.json. dryRun=true returns the create/skip diff (with signature + requirement checks) without writing. Unsigned packs are allowed here (file installs) - the UI warns; catalog installs go through /packs/catalog/install which enforces signatures.", + "operationId": "postPacksImport", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "confirm": { + "default": false, + "type": "boolean" + }, + "dryRun": { + "default": false, + "type": "boolean" + }, + "pack": {}, + "projectId": { + "type": "string" + } + }, + "required": [ + "projectId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackInstallResult" + } + } + }, + "description": "The diff alone on a dry run; the diff plus `created` and `install` when `confirm: true` wrote" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "`confirm: true` was not sent, so nothing was written (that body also carries `diff`)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No project with that id in the caller's scope" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a valid .dmpack file, or the pack claims a signature that fails verification" + } + }, + "tags": [ + "Packs" + ] + } + }, + "/packs/installed": { + "get": { + "description": "List Hub packs installed for the current scope.", + "operationId": "getPacksInstalled", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PackInstallListItem" + }, + "type": "array" + } + } + }, + "description": "Installed packs in scope, newest first" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Packs" + ] + } + }, + "/permissions": { + "get": { + "description": "Get the catalogue of assignable permission strings", + "operationId": "getPermissions", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PermissionCatalogue" + } + } + }, + "description": "Every assignable permission string, flat and grouped by resource" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Roles" + ] + } + }, + "/plans": { + "get": { + "description": "List plans in the active project", + "operationId": "getPlans", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Plan" + }, + "type": "array" + } + } + }, + "description": "Plans in the active project" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/capability-catalog": { + "get": { + "description": "Derive a plan's capability-catalog baseline from the project's templates", + "operationId": "getPlansCapability-catalog", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/CapabilityCatalogItem" + }, + "type": "array" + } + } + }, + "description": "One candidate per project template, name-ordered. Curation lives on the plan, never here" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/save": { + "post": { + "description": "Create a new plan", + "operationId": "postPlansSave", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdFrom": { + "type": "string" + }, + "env": { + "type": "string" + }, + "history": { + "default": [], + "items": { + "properties": { + "by": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "status": { + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "when": { + "type": "string" + } + }, + "required": [ + "status", + "when" + ], + "type": "object" + }, + "type": "array" + }, + "origin": { + "enum": [ + "chat", + "blueprint", + "intake", + "gap analysis" + ], + "type": "string" + }, + "owner": { + "type": "string" + }, + "spec": { + "properties": { + "approval": { + "properties": { + "reason": { + "type": "string" + }, + "role": { + "type": "string" + } + }, + "required": [ + "role", + "reason" + ], + "type": "object" + }, + "capabilities": { + "items": { + "properties": { + "expectedCount": { + "minimum": 0, + "type": "integer" + }, + "expectedSumCents": { + "type": "integer" + }, + "group": { + "type": "string" + }, + "hidden": { + "default": false, + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "setId": { + "type": "string" + }, + "source": { + "default": "derived", + "enum": [ + "derived", + "manual" + ], + "type": "string" + }, + "sumField": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + } + }, + "required": [ + "key", + "name" + ], + "type": "object" + }, + "type": "array" + }, + "constraints": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "coverage": { + "items": { + "properties": { + "count": { + "minimum": 0, + "type": "integer" + }, + "family": { + "type": "string" + }, + "items": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "family", + "count", + "items" + ], + "type": "object" + }, + "type": "array" + }, + "coverageConfig": { + "properties": { + "freshnessDays": { + "default": 14, + "minimum": 1, + "type": "integer" + }, + "mode": { + "default": "release", + "enum": [ + "release", + "migration" + ], + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "sumToleranceCents": { + "default": 0, + "minimum": 0, + "type": "integer" + }, + "targetSystem": { + "type": "string" + } + }, + "type": "object" + }, + "entities": { + "default": [], + "items": { + "properties": { + "endpointId": { + "type": "string" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + }, + "volume": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "name", + "template", + "volume", + "key" + ], + "type": "object" + }, + "type": "array" + }, + "expectations": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "flow": { + "items": { + "properties": { + "loop": { + "type": [ + "string", + "null" + ] + }, + "phase": { + "type": "string" + }, + "steps": { + "items": { + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "from": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "msg": { + "type": "string" + }, + "reachable": { + "type": "boolean" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "status": { + "enum": [ + "mapped", + "likely", + "custom", + "unknown" + ], + "type": "string" + }, + "to": { + "type": "string" + }, + "why": { + "type": "string" + } + }, + "required": [ + "from", + "to", + "msg", + "iface", + "kind", + "status" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "steps" + ], + "type": "object" + }, + "type": "array" + }, + "gaps": { + "default": [], + "items": { + "properties": { + "action": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "title": { + "type": "string" + }, + "type": { + "enum": [ + "connect", + "custom", + "input", + "blocker", + "gap", + "stale" + ], + "type": "string" + } + }, + "required": [ + "type", + "title" + ], + "type": "object" + }, + "type": "array" + }, + "integrations": { + "default": [], + "items": { + "properties": { + "access": { + "enum": [ + "read", + "write" + ], + "type": "string" + }, + "detail": { + "default": "", + "type": "string" + }, + "system": { + "type": "string" + }, + "via": { + "type": "string" + } + }, + "required": [ + "system", + "via", + "access" + ], + "type": "object" + }, + "type": "array" + }, + "kind": { + "enum": [ + "entity", + "flow", + "task", + "mapping" + ], + "type": "string" + }, + "lanes": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "lifecycle": { + "properties": { + "mode": { + "type": "string" + }, + "refresh": { + "type": "string" + }, + "replenish": { + "type": "string" + } + }, + "required": [ + "mode", + "refresh", + "replenish" + ], + "type": "object" + }, + "mappingConfig": { + "properties": { + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "maskingPolicyName": { + "type": "string" + }, + "sourceObject": { + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "targetObject": { + "type": "string" + }, + "targetSystem": { + "type": "string" + } + }, + "type": "object" + }, + "mappings": { + "items": { + "properties": { + "generate": { + "default": false, + "type": "boolean" + }, + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "sourceField": { + "type": "string" + }, + "status": { + "default": "open", + "enum": [ + "mapped", + "open", + "needs-review" + ], + "type": "string" + }, + "targetField": { + "type": "string" + }, + "transform": { + "default": "", + "type": "string" + } + }, + "required": [ + "targetField" + ], + "type": "object" + }, + "type": "array" + }, + "steps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "tasks": { + "items": { + "properties": { + "note": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "tasks": { + "items": { + "properties": { + "deps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "detail": { + "default": "", + "type": "string" + }, + "key": { + "type": "string" + }, + "priority": { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "title" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "tasks" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "properties": { + "by": { + "type": "string" + }, + "n": { + "minimum": 1, + "type": "integer" + }, + "when": { + "type": "string" + } + }, + "required": [ + "n", + "by", + "when" + ], + "type": "object" + } + }, + "type": "object" + }, + "status": { + "default": "draft", + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "summary": { + "type": "string" + }, + "targets": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "title": { + "minLength": 1, + "type": "string" + }, + "write": { + "default": false, + "type": "boolean" + } + }, + "required": [ + "title" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plan" + } + } + }, + "description": "The created plan" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The creating team member could not be resolved" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{id}": { + "get": { + "description": "Get a plan by id", + "operationId": "getPlansById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plan" + } + } + }, + "description": "The plan" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}": { + "delete": { + "description": "Delete a plan", + "operationId": "deletePlansByPlanId", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanDeleteResult" + } + } + }, + "description": "The plan was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + }, + "patch": { + "description": "Update a plan", + "operationId": "patchPlansByPlanId", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdFrom": { + "type": "string" + }, + "env": { + "type": "string" + }, + "history": { + "default": [], + "items": { + "properties": { + "by": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "status": { + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "when": { + "type": "string" + } + }, + "required": [ + "status", + "when" + ], + "type": "object" + }, + "type": "array" + }, + "origin": { + "enum": [ + "chat", + "blueprint", + "intake", + "gap analysis" + ], + "type": "string" + }, + "owner": { + "type": "string" + }, + "spec": { + "properties": { + "approval": { + "properties": { + "reason": { + "type": "string" + }, + "role": { + "type": "string" + } + }, + "required": [ + "role", + "reason" + ], + "type": "object" + }, + "capabilities": { + "items": { + "properties": { + "expectedCount": { + "minimum": 0, + "type": "integer" + }, + "expectedSumCents": { + "type": "integer" + }, + "group": { + "type": "string" + }, + "hidden": { + "default": false, + "type": "boolean" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "setId": { + "type": "string" + }, + "source": { + "default": "derived", + "enum": [ + "derived", + "manual" + ], + "type": "string" + }, + "sumField": { + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + } + }, + "required": [ + "key", + "name" + ], + "type": "object" + }, + "type": "array" + }, + "constraints": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "coverage": { + "items": { + "properties": { + "count": { + "minimum": 0, + "type": "integer" + }, + "family": { + "type": "string" + }, + "items": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "family", + "count", + "items" + ], + "type": "object" + }, + "type": "array" + }, + "coverageConfig": { + "properties": { + "freshnessDays": { + "default": 14, + "minimum": 1, + "type": "integer" + }, + "mode": { + "default": "release", + "enum": [ + "release", + "migration" + ], + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "sumToleranceCents": { + "default": 0, + "minimum": 0, + "type": "integer" + }, + "targetSystem": { + "type": "string" + } + }, + "type": "object" + }, + "entities": { + "default": [], + "items": { + "properties": { + "endpointId": { + "type": "string" + }, + "key": { + "type": "string" + }, + "name": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "template": { + "type": "string" + }, + "templateId": { + "type": "string" + }, + "volume": { + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "name", + "template", + "volume", + "key" + ], + "type": "object" + }, + "type": "array" + }, + "expectations": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "flow": { + "items": { + "properties": { + "loop": { + "type": [ + "string", + "null" + ] + }, + "phase": { + "type": "string" + }, + "steps": { + "items": { + "properties": { + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "from": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "msg": { + "type": "string" + }, + "reachable": { + "type": "boolean" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "status": { + "enum": [ + "mapped", + "likely", + "custom", + "unknown" + ], + "type": "string" + }, + "to": { + "type": "string" + }, + "why": { + "type": "string" + } + }, + "required": [ + "from", + "to", + "msg", + "iface", + "kind", + "status" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "steps" + ], + "type": "object" + }, + "type": "array" + }, + "gaps": { + "default": [], + "items": { + "properties": { + "action": { + "type": "string" + }, + "iface": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "scaffold": { + "properties": { + "entity": { + "type": "string" + }, + "fields": { + "items": { + "type": "string" + }, + "type": "array" + }, + "ops": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "entity", + "ops", + "fields" + ], + "type": "object" + }, + "title": { + "type": "string" + }, + "type": { + "enum": [ + "connect", + "custom", + "input", + "blocker", + "gap", + "stale" + ], + "type": "string" + } + }, + "required": [ + "type", + "title" + ], + "type": "object" + }, + "type": "array" + }, + "integrations": { + "default": [], + "items": { + "properties": { + "access": { + "enum": [ + "read", + "write" + ], + "type": "string" + }, + "detail": { + "default": "", + "type": "string" + }, + "system": { + "type": "string" + }, + "via": { + "type": "string" + } + }, + "required": [ + "system", + "via", + "access" + ], + "type": "object" + }, + "type": "array" + }, + "kind": { + "enum": [ + "entity", + "flow", + "task", + "mapping" + ], + "type": "string" + }, + "lanes": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "lifecycle": { + "properties": { + "mode": { + "type": "string" + }, + "refresh": { + "type": "string" + }, + "replenish": { + "type": "string" + } + }, + "required": [ + "mode", + "refresh", + "replenish" + ], + "type": "object" + }, + "mappingConfig": { + "properties": { + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "maskingPolicyName": { + "type": "string" + }, + "sourceObject": { + "type": "string" + }, + "sourceSystem": { + "type": "string" + }, + "targetObject": { + "type": "string" + }, + "targetSystem": { + "type": "string" + } + }, + "type": "object" + }, + "mappings": { + "items": { + "properties": { + "generate": { + "default": false, + "type": "boolean" + }, + "keyMapName": { + "type": "string" + }, + "maskingPolicyId": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "sourceField": { + "type": "string" + }, + "status": { + "default": "open", + "enum": [ + "mapped", + "open", + "needs-review" + ], + "type": "string" + }, + "targetField": { + "type": "string" + }, + "transform": { + "default": "", + "type": "string" + } + }, + "required": [ + "targetField" + ], + "type": "object" + }, + "type": "array" + }, + "steps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "tasks": { + "items": { + "properties": { + "note": { + "type": "string" + }, + "phase": { + "type": "string" + }, + "tasks": { + "items": { + "properties": { + "deps": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "detail": { + "default": "", + "type": "string" + }, + "key": { + "type": "string" + }, + "priority": { + "enum": [ + "P0", + "P1", + "P2", + "P3" + ], + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "title" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "phase", + "tasks" + ], + "type": "object" + }, + "type": "array" + }, + "version": { + "properties": { + "by": { + "type": "string" + }, + "n": { + "minimum": 1, + "type": "integer" + }, + "when": { + "type": "string" + } + }, + "required": [ + "n", + "by", + "when" + ], + "type": "object" + } + }, + "type": "object" + }, + "status": { + "default": "draft", + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "summary": { + "type": "string" + }, + "targets": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "title": { + "minLength": 1, + "type": "string" + }, + "write": { + "default": false, + "type": "boolean" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plan" + } + } + }, + "description": "The updated plan" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/check": { + "post": { + "description": "Run a plan's coverage checks and store the graded result as a check run", + "operationId": "postPlansByPlanIdCheck", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanRun" + } + } + }, + "description": "The completed check run, graded synchronously - no polling needed" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The plan has no capabilities in scope; derive the catalog and save it to the spec first" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The check engine failed; the run is persisted as failed" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/run": { + "post": { + "description": "Execute an entity plan and produce artifacts", + "operationId": "postPlansByPlanIdRun", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanRunAccepted" + } + } + }, + "description": "The run was created and dispatched; poll GET /plans/:planId/runs/:runId" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The plan is not runnable: a task plan, a mapping plan, a flow plan, one with no entities, or one whose entities have no matching template (that body also carries `entities`, the unresolved names)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/runs": { + "get": { + "description": "List a plan's runs", + "operationId": "getPlansByPlanIdRuns", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PlanRun" + }, + "type": "array" + } + } + }, + "description": "The plan's runs, newest first. `files` is never present here" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/runs/{runId}": { + "get": { + "description": "Get a plan run with logs + artifacts", + "operationId": "getPlansByPlanIdRunsByRunId", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "runId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanRun" + } + } + }, + "description": "The run, including `files` with freshly re-signed download URLs" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope, or no run with that id on the plan" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/runs/{runId}/complete": { + "post": { + "description": "Finalize a plan run's terminal state (agent bookkeeping)", + "operationId": "postPlansByPlanIdRunsByRunIdComplete", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "runId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "error": { + "type": "string" + }, + "rows": { + "default": 0, + "minimum": 0, + "type": "integer" + }, + "status": { + "enum": [ + "completed", + "failed" + ], + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanRunAck" + } + } + }, + "description": "The run was finalized and rolled up to the plan" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope, or no run with that id on the plan" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/runs/{runId}/files": { + "post": { + "description": "Record a generated artifact for a plan run (agent bookkeeping)", + "operationId": "postPlansByPlanIdRunsByRunIdFiles", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "runId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "content": { + "type": "string" + }, + "entity": { + "minLength": 1, + "type": "string" + }, + "filename": { + "minLength": 1, + "type": "string" + }, + "mimeType": { + "default": "application/json", + "type": "string" + } + }, + "required": [ + "entity", + "filename", + "content" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanRunFileAck" + } + } + }, + "description": "The artifact was stored and recorded against the run" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope, or no run with that id on the plan" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The artifact could not be uploaded to blob storage" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/runs/{runId}/log": { + "post": { + "description": "Append a log line to a plan run (agent bookkeeping)", + "operationId": "postPlansByPlanIdRunsByRunIdLog", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "runId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "level": { + "default": "info", + "enum": [ + "info", + "warn", + "error" + ], + "type": "string" + }, + "message": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "message" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanRunAck" + } + } + }, + "description": "The log line was appended" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope, or no run with that id on the plan" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/signoffs": { + "get": { + "description": "List a plan's sign-off artifacts", + "operationId": "getPlansByPlanIdSignoffs", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PlanSignoff" + }, + "type": "array" + } + } + }, + "description": "The plan's sign-offs, newest first" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + }, + "post": { + "description": "Record an immutable sign-off on a coverage check run (waiver note required on a non-green verdict)", + "operationId": "postPlansByPlanIdSignoffs", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "note": { + "maxLength": 4000, + "type": "string" + }, + "runId": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "runId" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlanSignoff" + } + } + }, + "description": "The recorded sign-off. Sign-offs are immutable" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The run is not a check run, or a non-green verdict was signed off without a waiver note" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope, or no run with that id on the plan" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The check run has not completed, carries no readable results, or is already signed off" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/plans/{planId}/status": { + "post": { + "description": "Transition a plan's status and optionally append a run-history entry", + "operationId": "postPlansByPlanIdStatus", + "parameters": [ + { + "in": "path", + "name": "planId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "history": { + "properties": { + "by": { + "type": "string" + }, + "note": { + "default": "", + "type": "string" + }, + "status": { + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + }, + "when": { + "type": "string" + } + }, + "required": [ + "status", + "when" + ], + "type": "object" + }, + "status": { + "enum": [ + "draft", + "approved", + "running", + "completed", + "failed" + ], + "type": "string" + } + }, + "required": [ + "status" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Plan" + } + } + }, + "description": "The plan at its new status, with the appended history entry" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No plan with that id in the caller's scope" + } + }, + "tags": [ + "Plans" + ] + } + }, + "/preview": { + "post": { + "description": "Preview external endpoint (proxy for mixed content)", + "operationId": "postPreview", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreviewResult" + } + } + }, + "description": "The upstream response. Note the 200 here means the PROXY succeeded - read `status` for what the endpoint itself answered" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "URL or method missing, a malformed URL, or a malformed JSON body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "An endpointId was supplied but no endpoint with that id exists" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The proxy threw" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Could not connect to the external endpoint (that body carries `error: \"FETCH_ERROR\"`)" + } + }, + "tags": [ + "Preview" + ] + } + }, + "/projects": { + "get": { + "description": "Get all projects", + "operationId": "getProjects", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Project" + }, + "type": "array" + } + } + }, + "description": "Projects the caller can access" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Projects" + ] + }, + "post": { + "description": "Create a new project", + "operationId": "postProjects", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project" + } + } + }, + "description": "The created project" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Projects" + ] + } + }, + "/projects/{id}": { + "delete": { + "description": "Delete a project", + "operationId": "deleteProjectsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The project was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No project with that id in the caller's scope" + } + }, + "tags": [ + "Projects" + ] + }, + "get": { + "description": "Get a project by id", + "operationId": "getProjectsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project" + } + } + }, + "description": "The project" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No project with that id in the caller's scope" + } + }, + "tags": [ + "Projects" + ] + }, + "put": { + "description": "Update a project", + "operationId": "putProjectsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "avatar": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project" + } + } + }, + "description": "The updated project" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No project with that id in the caller's scope" + } + }, + "tags": [ + "Projects" + ] + } + }, + "/roles": { + "get": { + "description": "Get all roles for a team", + "operationId": "getRoles", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Role" + }, + "type": "array" + } + } + }, + "description": "System and custom roles for the team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Roles" + ] + }, + "post": { + "description": "Create a custom role", + "operationId": "postRoles", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "description": { + "maxLength": 280, + "type": "string" + }, + "name": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "permissions": { + "default": [], + "items": { + "type": "string" + }, + "type": "array" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "teamId", + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Role" + } + } + }, + "description": "The created custom role" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "A role with that name already exists on the team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Roles" + ] + } + }, + "/roles/{id}": { + "delete": { + "description": "Delete a custom role", + "operationId": "deleteRolesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The custom role was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Roles" + ] + }, + "put": { + "description": "Update a custom role", + "operationId": "putRolesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "description": { + "maxLength": 280, + "type": [ + "string", + "null" + ] + }, + "name": { + "maxLength": 64, + "minLength": 1, + "type": "string" + }, + "permissions": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Role" + } + } + }, + "description": "The updated custom role" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Roles" + ] + } + }, + "/scenario-files": { + "post": { + "description": "Create scenario file from base64 content", + "operationId": "postScenario-files", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "content": { + "type": "string" + }, + "description": { + "type": "string" + }, + "folderId": { + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "scenarioId": { + "type": "string" + }, + "size": { + "type": "number" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "content", + "scenarioId", + "teamId", + "size" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioFileCreated" + } + } + }, + "description": "The stored file, under this endpoint's own field names (`name`, `filePath`)" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The decoded content is over the size cap, or the workspace quota is exhausted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The authenticated scope does not cover the scenario's team or project" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The upload to blob storage failed" + } + }, + "tags": [ + "Internal Workspace" + ] + } + }, + "/scenarios": { + "get": { + "description": "Get all scenarios", + "operationId": "getScenarios", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Scenario" + }, + "type": "array" + } + } + }, + "description": "Scenarios in scope, with freshly signed script URLs" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Scenarios" + ] + }, + "post": { + "description": "Create a new scenario generated by AI", + "operationId": "postScenarios", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdAt": { + "type": "string" + }, + "createdBy": { + "description": "A valid team member id", + "type": "string" + }, + "description": { + "description": "Short description of what this scenario does", + "type": "string" + }, + "name": { + "type": "string" + }, + "presignedUrl": { + "type": "string" + }, + "projectId": { + "description": "The project ID this scenario belongs to", + "type": "string" + }, + "requirementsUrl": { + "type": "string" + }, + "teamId": { + "description": "The team ID this scenario belongs to", + "type": "string" + }, + "timeoutSeconds": { + "description": "Execution timeout in seconds (60–3600)", + "maximum": 3600, + "minimum": 60, + "type": "integer" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scenario" + } + } + }, + "description": "The created scenario" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The creating team member could not be resolved" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/execute": { + "post": { + "description": "Execute a scenario by queueing it for execution. By default waits for completion (async=false), set async=true for immediate return with jobId.", + "operationId": "postScenariosExecute", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "async": { + "default": false, + "type": "boolean" + }, + "code": { + "description": "Optional inline code to execute instead of saved script", + "type": "string" + }, + "projectId": { + "description": "The project ID this scenario belongs to", + "type": "string" + }, + "scenarioId": { + "description": "The scenario ID to execute", + "type": "string" + }, + "source": { + "default": "manual", + "description": "What initiated the run. UI clients omit this (defaults to 'manual'). The chat agent must send 'agent' so the run can be visually distinguished in scenario history.", + "enum": [ + "manual", + "agent", + "mcp" + ], + "type": "string" + } + }, + "required": [ + "projectId" + ], + "type": "object" + } + } + } + }, + "responses": {}, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/from-chat": { + "post": { + "description": "Generate a repeatable scenario from a chat thread. Reads the chat's agent-session turns (or a client-supplied thread snapshot), converts them into a single Python script via the LLM, and saves it like /scenarios/save.", + "operationId": "postScenariosFrom-chat", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "chatId": { + "description": "The chat whose thread should become a scenario", + "type": "string" + }, + "projectId": { + "description": "The project the chat and scenario belong to", + "type": "string" + }, + "thread": { + "description": "Optional client-side thread snapshot (richer than the persisted row: includes tool-call summaries)", + "properties": { + "turns": { + "items": { + "properties": { + "prompt": { + "type": "string" + }, + "reply": { + "type": "string" + }, + "toolSummaries": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "prompt", + "reply" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "turns" + ], + "type": "object" + }, + "timeoutSeconds": { + "description": "Execution timeout in seconds (60–3600)", + "maximum": 3600, + "minimum": 60, + "type": "integer" + } + }, + "required": [ + "chatId", + "projectId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scenario" + } + } + }, + "description": "The scenario created from the chat thread" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No team member for the caller, or the chat has no completed agent turns to package" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No chat with that id in this project" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Generation, upload or persistence failed" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/jobs": { + "get": { + "description": "List active and recent scenario executions from the queue", + "operationId": "getScenariosJobs", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioJobs" + } + } + }, + "description": "Queued and running executions, active first" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The job queue could not be read" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/jobs/{jobId}/cancel": { + "post": { + "description": "Cancel a running scenario execution job", + "operationId": "postScenariosJobsByJobIdCancel", + "parameters": [ + { + "in": "path", + "name": "jobId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "Cancellation was accepted; `message` describes what happened" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No job id, or the job is not in a cancellable state" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The job is not in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Cancellation failed" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/jobs/{jobId}/logs/stream": { + "get": { + "description": "Stream logs for a running job via Server-Sent Events", + "operationId": "getScenariosJobsByJobIdLogsStream", + "parameters": [ + { + "in": "path", + "name": "jobId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "text/event-stream": { + "schema": { + "type": "string" + } + } + }, + "description": "An SSE stream of `log` events (data: one log line) terminated by a single `end` event" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No job id in the path" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/jobs/{jobId}/status": { + "get": { + "description": "Get the status of a queued job", + "operationId": "getScenariosJobsByJobIdStatus", + "parameters": [ + { + "in": "path", + "name": "jobId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioJobStatus" + } + } + }, + "description": "The run's current state, output and log lines" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No job id in the path" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No such job on the queue and no ScenarioLog row for it" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Reading the job status failed" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/save": { + "post": { + "description": "Save a scenario coded by user", + "operationId": "postScenariosSave", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "code": { + "type": "string" + }, + "name": { + "type": "string" + }, + "timeoutSeconds": { + "description": "Execution timeout in seconds (60–3600)", + "maximum": 3600, + "minimum": 60, + "type": "integer" + } + }, + "required": [ + "code" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scenario" + } + } + }, + "description": "The saved scenario" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The creating team member could not be resolved" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The scenario could not be saved" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/{id}": { + "delete": { + "description": "Delete a scenario", + "operationId": "deleteScenariosById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The scenario and its stored script files were deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Deleting the scenario or its S3 files failed" + } + }, + "tags": [ + "Scenarios" + ] + }, + "get": { + "description": "Get a scenario by id", + "operationId": "getScenariosById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scenario" + } + } + }, + "description": "The scenario, with freshly signed script URLs" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + } + }, + "tags": [ + "Scenarios" + ] + }, + "patch": { + "description": "Update a scenario", + "operationId": "patchScenariosById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdAt": { + "type": "string" + }, + "createdBy": { + "description": "A valid team member id", + "type": "string" + }, + "description": { + "description": "Short description of what this scenario does", + "type": "string" + }, + "name": { + "type": "string" + }, + "presignedUrl": { + "type": "string" + }, + "projectId": { + "description": "The project ID this scenario belongs to", + "type": "string" + }, + "requirementsUrl": { + "type": "string" + }, + "teamId": { + "description": "The team ID this scenario belongs to", + "type": "string" + }, + "timeoutSeconds": { + "description": "Execution timeout in seconds (60–3600)", + "maximum": 3600, + "minimum": 60, + "type": "integer" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Scenario" + } + } + }, + "description": "The updated scenario" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No team member for the caller in the scenario's team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The update failed" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/{id}/diagram/regenerate": { + "post": { + "description": "Regenerate the Mermaid flow diagram for a scenario. Accepts an optional { code } body (the current editor contents); otherwise the stored script is read from blob storage.", + "operationId": "postScenariosByIdDiagramRegenerate", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioDiagramRegenerateAccepted" + } + } + }, + "description": "Regeneration was queued; poll the scenario for `diagramStatus`" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The scenario has no code to diagram yet" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Regeneration could not be started" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/{id}/environment-variables": { + "get": { + "description": "Get environment variables for a scenario", + "operationId": "getScenariosByIdEnvironment-variables", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioEnvironmentVariableList" + } + } + }, + "description": "The scenario's environment variables, as an array of key/value pairs" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Reading the variables failed" + } + }, + "tags": [ + "Scenarios" + ] + }, + "patch": { + "description": "Update environment variables for a scenario", + "operationId": "patchScenariosByIdEnvironment-variables", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "environmentVariables": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + } + }, + "required": [ + "environmentVariables" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioEnvironmentVariables" + } + } + }, + "description": "The merged variable map after the update (a map, not the array the GET returns)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The update failed" + } + }, + "tags": [ + "Scenarios" + ] + }, + "post": { + "description": "Add an environment variable to a scenario", + "operationId": "postScenariosByIdEnvironment-variables", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "key": { + "minLength": 1, + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "key", + "value" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioEnvironmentVariables" + } + } + }, + "description": "The full variable map after the addition (a map, not the array the GET returns)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The variable could not be added" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/{id}/environment-variables/{key}": { + "delete": { + "description": "Delete an environment variable from a scenario", + "operationId": "deleteScenariosByIdEnvironment-variablesByKey", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "key", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioEnvironmentVariables" + } + } + }, + "description": "The remaining variable map after the removal (a map, not the array the GET returns)" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No variable key in the path" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No scenario with that id in the caller's scope" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The variable could not be removed" + } + }, + "tags": [ + "Scenarios" + ] + } + }, + "/scenarios/{scenarioId}/files": { + "get": { + "description": "List all workspace files for a scenario", + "operationId": "getScenariosByScenarioIdFiles", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceListing" + } + } + }, + "description": "Workspace files, with the storage quota they count against" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the scenario's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Scenario not found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The files could not be listed" + } + }, + "tags": [ + "Workspace Files" + ] + } + }, + "/scenarios/{scenarioId}/files/upload": { + "post": { + "description": "Upload a file to the scenario workspace", + "operationId": "postScenariosByScenarioIdFilesUpload", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceUploadResult" + } + } + }, + "description": "The stored file" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No file, an unknown folder, an oversized file, or the workspace quota is exhausted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the scenario's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Scenario not found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The upload failed" + } + }, + "tags": [ + "Workspace Files" + ] + } + }, + "/scenarios/{scenarioId}/files/{fileId}": { + "delete": { + "description": "Delete a workspace file", + "operationId": "deleteScenariosByScenarioIdFilesByFileId", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "fileId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResult" + } + } + }, + "description": "The file was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the scenario's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "File not found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The delete failed" + } + }, + "tags": [ + "Workspace Files" + ] + }, + "get": { + "description": "Get file metadata and download URL", + "operationId": "getScenariosByScenarioIdFilesByFileId", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "fileId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioFile" + } + } + }, + "description": "File metadata with a freshly signed download URL" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the scenario's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "File not found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The file could not be read" + } + }, + "tags": [ + "Workspace Files" + ] + } + }, + "/scenarios/{scenarioId}/storage": { + "get": { + "description": "Get storage usage for a scenario workspace", + "operationId": "getScenariosByScenarioIdStorage", + "parameters": [ + { + "in": "path", + "name": "scenarioId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkspaceStorage" + } + } + }, + "description": "Bytes used, the limit, and the rounded percentage" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the scenario's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Scenario not found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Storage info could not be read" + } + }, + "tags": [ + "Workspace Files" + ] + } + }, + "/schema-graph/{id}/diff": { + "post": { + "description": "Compare the endpoint's cached SAP/OData schema graph against live $metadata without updating the cache. Body (optional): { metadataUrl } to override the derived service-root URL.", + "operationId": "postSchema-graphByIdDiff", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphDiffResult" + } + } + }, + "description": "The cached graph compared against live $metadata. The cache is NOT updated" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, or no graph cached for it yet" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The live document is not parseable as EDMX (that body also carries `metadataUrl`)" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The $metadata fetch failed (that body also carries `metadataUrl`)" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/entity/{name}": { + "get": { + "description": "The 1-hop neighborhood of an entity: properties (with labels, types, writability), keys, and incoming/outgoing navigations. Accepts the EntityType or EntitySet name.", + "operationId": "getSchema-graphByIdEntityByName", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphEntityContext" + } + } + }, + "description": "The entity plus its incoming and outgoing navigations" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, no graph cached for it, or no such entity in the graph" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/entity/{name}/connect": { + "post": { + "description": "Wire a template's lookup/code fields to LIVE SAP data (port of datamaker-sap-cli's `sap connect`). Creates a DataMaker endpoint for the entity set (reusing this endpoint's credentials) and upgrades the template's Custom/Words fields to 'API Response' generators that fetch from it at generation time. Body: { templateId } (required). Returns the new endpoint id + upgraded field names.", + "operationId": "postSchema-graphByIdEntityByNameConnect", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphConnectResult" + } + } + }, + "description": "The bridge endpoint that was created and the template fields wired to it" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "templateId was not sent" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, no graph cached for it, no such entity in the graph, or no such template in this project" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/entity/{name}/preview": { + "post": { + "description": "Preview real rows from the entity set (endpoint credentials), with OData metadata stripped - the raw data, not mined statistics. Use to eyeball what the system actually contains before building a template. Body (optional): { top } rows (default 10, max 50). Port of datamaker-sap-cli's `sap preview`.", + "operationId": "postSchema-graphByIdEntityByNamePreview", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphPreviewResult" + } + } + }, + "description": "Real rows with OData bookkeeping keys stripped" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, no graph cached for it, or no such entity in the graph" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Reading live rows from the entity set failed" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/entity/{name}/sample": { + "post": { + "description": "Sample live rows from the entity set (endpoint credentials) and mine per-field value statistics: distinct values, frequencies, and which fields look like SAP code-lists (enum-like). Also lists the system's ValueHelp/code-list entity sets. Body (optional): { top } rows to sample (default 50, max 500).", + "operationId": "postSchema-graphByIdEntityByNameSample", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphSampleResult" + } + } + }, + "description": "Per-field value statistics over the sampled rows, plus the system's value-help sets" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, no graph cached for it, or no such entity in the graph" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Sampling live rows from the entity set failed" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/entity/{name}/template": { + "post": { + "description": "Create a DataMaker template from a schema-graph entity: every property becomes a field with a generator inferred from its EDM type + SAP naming heuristics (port of datamaker-sap-cli's transform). Body (optional): { name } for the template name. Returns the created template id.", + "operationId": "postSchema-graphByIdEntityByNameTemplate", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "name", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphTemplateResult" + } + } + }, + "description": "The created template's identity - fetch GET /templates/:id for the fields themselves" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, no graph cached for it, or no such entity in the graph" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "`useSamples` was sent and sampling live rows failed" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/path": { + "get": { + "description": "Shortest navigation path between two entities (how they relate), e.g. how SalesOrder connects to Product. Query: ?from=&to=", + "operationId": "getSchema-graphByIdPath", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphPathResult" + } + } + }, + "description": "The hop list. An empty array means `from` and `to` resolved to the same entity" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "?from= or ?to= is missing" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, no graph cached for it, or the two entities are not connected" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/refresh": { + "post": { + "description": "Fetch the endpoint's OData $metadata (with its stored credentials), parse it into a schema knowledge graph and cache it. Body (optional): { metadataUrl } to override the derived service-root URL.", + "operationId": "postSchema-graphByIdRefresh", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphRefreshResult" + } + } + }, + "description": "The graph was parsed and cached" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The document fetched is not parseable as EDMX (that body also carries `metadataUrl`)" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The $metadata fetch failed (that body also carries `metadataUrl`)" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/search": { + "get": { + "description": "Search the endpoint's cached schema graph for entities by name, business label (sap:label) or entity-set name. Query: ?q=", + "operationId": "getSchema-graphByIdSearch", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SchemaGraphSearchResult" + } + } + }, + "description": "Up to 10 hits, best first. An empty or missing ?q= returns no results rather than everything" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope, or no graph cached for it yet" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/schema-graph/{id}/services": { + "get": { + "description": "List the OData services registered on the SAP system this endpoint belongs to (Gateway CATALOGSERVICE), using the endpoint's stored credentials. Query: ?q= filters by id/description; ?status=active|inactive live-probes each service for reachability (port of the CLI's `sap catalog --active`); ?limit= caps the number of services returned.", + "operationId": "getSchema-graphByIdServices", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SapServiceCatalog" + } + } + }, + "description": "The Gateway catalog, mapped into DataMaker's shape. The probe counts are present only when ?status= asked for a live reachability probe" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No endpoint with that id in the caller's scope" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The catalog fetch failed - probably not a SAP Gateway system (that body also carries `catalogUrl`)" + } + }, + "tags": [ + "SchemaGraph" + ] + } + }, + "/sets": { + "get": { + "description": "Get all saved sets for the caller's team/project scope", + "operationId": "getSets", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Set" + }, + "type": "array" + } + } + }, + "description": "Sets in scope, newest first" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Sets" + ] + }, + "post": { + "description": "Create (save) a new set scoped to the caller's team/project", + "operationId": "postSets", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": { + "description": "The saved rows payload (JSON)" + }, + "description": { + "description": "Short description of the set", + "type": "string" + }, + "locked": { + "description": "Freeze the set on creation (snapshot semantics)", + "type": "boolean" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "projectId": { + "description": "The project ID this set belongs to", + "type": "string" + }, + "rowCount": { + "description": "Row count; derived from `data` when omitted", + "minimum": 0, + "type": "integer" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Set" + } + } + }, + "description": "The created set" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The payload exceeds the inline size cap (10,000 rows / 5 MB)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The set could not be created" + } + }, + "tags": [ + "Sets" + ] + } + }, + "/sets/{id}": { + "delete": { + "description": "Delete a saved set", + "operationId": "deleteSetsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The set was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The set is locked; unlock it before deleting" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The set could not be deleted" + } + }, + "tags": [ + "Sets" + ] + }, + "get": { + "description": "Get a saved set by id", + "operationId": "getSetsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetDetail" + } + } + }, + "description": "The set, enriched with the creator's display name" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No set with that id in the caller's scope" + } + }, + "tags": [ + "Sets" + ] + }, + "patch": { + "description": "Update a saved set", + "operationId": "patchSetsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "data": {}, + "description": { + "type": [ + "string", + "null" + ] + }, + "locked": { + "description": "Lock (freeze) or unlock the set. Locking is always allowed; other edits are rejected while locked.", + "type": "boolean" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "rowCount": { + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Set" + } + } + }, + "description": "The updated set" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The payload exceeds the inline size cap (10,000 rows / 5 MB)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The set is locked; unlock it before editing" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The set could not be updated" + } + }, + "tags": [ + "Sets" + ] + } + }, + "/setup/teams": { + "post": { + "description": "Create a new team while also creating teamMember and project", + "operationId": "postSetupTeams", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamSetupResult" + } + } + }, + "description": "The team, the caller's membership and the first project, all created in one transaction" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No user id on the authenticated scope" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Teams" + ] + } + }, + "/shortcuts": { + "get": { + "description": "Get all shortcuts", + "operationId": "getShortcuts", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Shortcut" + }, + "type": "array" + } + } + }, + "description": "The caller's shortcut bindings" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Shortcuts" + ] + }, + "post": { + "description": "Create a new shortcut", + "operationId": "postShortcuts", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "context": { + "enum": [ + "GLOBAL", + "TEMPLATE_PAGE", + "EDIT_TEMPLATE" + ], + "type": "string" + }, + "function": { + "type": "string" + }, + "id": { + "type": "string" + }, + "keys": { + "items": { + "type": "string" + }, + "type": "array" + }, + "userId": { + "type": "string" + } + }, + "required": [ + "id", + "function", + "context", + "keys", + "userId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Shortcut" + } + } + }, + "description": "The created binding" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Shortcuts" + ] + } + }, + "/shortcuts/{id}": { + "delete": { + "description": "Delete a shortcut", + "operationId": "deleteShortcutsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The binding was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No shortcut with that id" + } + }, + "tags": [ + "Shortcuts" + ] + }, + "put": { + "description": "Update a shortcut", + "operationId": "putShortcutsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "context": { + "enum": [ + "GLOBAL", + "TEMPLATE_PAGE", + "EDIT_TEMPLATE" + ], + "type": "string" + }, + "function": { + "type": "string" + }, + "id": { + "type": "string" + }, + "keys": { + "items": { + "type": "string" + }, + "type": "array" + }, + "userId": { + "type": "string" + } + }, + "required": [ + "id", + "function", + "context", + "keys", + "userId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Shortcut" + } + } + }, + "description": "The updated binding" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Shortcuts" + ] + } + }, + "/skills": { + "get": { + "description": "List the team's skills (reusable agent instructions) plus the read-only built-in skills. Skills are team-wide. Query: ?teamId= (required).", + "operationId": "getSkills", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Skill" + }, + "type": "array" + } + } + }, + "description": "Team skills followed by the built-ins. Mixed list - discriminate on `scope`" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Skills" + ] + }, + "post": { + "description": "Create a team skill.", + "operationId": "postSkills", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "body": { + "minLength": 1, + "type": "string" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "minLength": 1, + "type": "string" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "description", + "body", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamSkill" + } + } + }, + "description": "The created skill" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Skills" + ] + } + }, + "/skills/import": { + "post": { + "description": "Create a team skill from an uploaded SKILL.md file (frontmatter name/title + description, markdown body).", + "operationId": "postSkillsImport", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "content": { + "minLength": 1, + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "projectId": { + "type": [ + "string", + "null" + ] + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "content", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamSkill" + } + } + }, + "description": "The skill created from the uploaded SKILL.md" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The file could not be parsed as SKILL.md" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Skills" + ] + } + }, + "/skills/{id}": { + "delete": { + "description": "Delete a team skill.", + "operationId": "deleteSkillsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The skill was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No skill with that id for this team" + } + }, + "tags": [ + "Skills" + ] + }, + "get": { + "description": "Get a single team skill by ID.", + "operationId": "getSkillsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamSkill" + } + } + }, + "description": "The team skill" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No skill with that id for this team" + } + }, + "tags": [ + "Skills" + ] + }, + "put": { + "description": "Update a team skill.", + "operationId": "putSkillsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "body": { + "minLength": 1, + "type": "string" + }, + "description": { + "minLength": 1, + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "name": { + "minLength": 1, + "type": "string" + } + }, + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamSkill" + } + } + }, + "description": "The updated skill" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No skill with that id for this team" + } + }, + "tags": [ + "Skills" + ] + } + }, + "/skills/{id}/export": { + "get": { + "description": "Download a team skill as a shareable SKILL.md file (frontmatter + markdown body).", + "operationId": "getSkillsByIdExport", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "text/markdown": { + "schema": { + "type": "string" + } + } + }, + "description": "The SKILL.md document: YAML frontmatter followed by the markdown body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No skill with that id in the caller's scope" + } + }, + "tags": [ + "Skills" + ] + } + }, + "/teamMembers": { + "get": { + "description": "Get all team members", + "operationId": "getTeamMembers", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TeamMember" + }, + "type": "array" + } + } + }, + "description": "Memberships in the caller's accessible teams. Each also carries its Team, Shortcuts, User and CustomRoles relations" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Team Members" + ] + }, + "post": { + "description": "Create a new team member", + "operationId": "postTeamMembers", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "id": { + "type": "string" + }, + "role": { + "enum": [ + "MEMBER", + "ADMIN", + "OWNER" + ], + "type": "string" + }, + "teamId": { + "type": "string" + }, + "userId": { + "type": "string" + } + }, + "required": [ + "userId", + "teamId", + "role" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamMember" + } + } + }, + "description": "The created membership" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No seat available on the team's plan" + } + }, + "tags": [ + "Team Members" + ] + } + }, + "/teamMembers/invite": { + "post": { + "description": "Invite a new team member by email", + "operationId": "postTeamMembersInvite", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "email": { + "format": "email", + "type": "string" + }, + "role": { + "default": "MEMBER", + "enum": [ + "MEMBER", + "ADMIN", + "OWNER" + ], + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "email", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberInviteResult" + } + } + }, + "description": "The created membership, flattened with the invited user's first and last name" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "That user is already a member of the team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "The team has no seat available under its current license" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiMessageError" + } + } + }, + "description": "No user with that email" + } + }, + "tags": [ + "Team Members" + ] + } + }, + "/teamMembers/{id}": { + "delete": { + "description": "Delete a team member", + "operationId": "deleteTeamMembersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The membership was removed" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No team member with that id" + } + }, + "tags": [ + "Team Members" + ] + }, + "put": { + "description": "Update a team member", + "operationId": "putTeamMembersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "role": { + "enum": [ + "MEMBER", + "ADMIN", + "OWNER" + ], + "type": "string" + } + }, + "required": [ + "role" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamMember" + } + } + }, + "description": "The updated membership" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Team Members" + ] + } + }, + "/teamMembers/{id}/roles": { + "post": { + "description": "Assign a custom role to a team member", + "operationId": "postTeamMembersByIdRoles", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "roleId": { + "type": "string" + } + }, + "required": [ + "roleId" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberRoleAssignment" + } + } + }, + "description": "The role assignment. Idempotent - re-assigning an existing role returns the same row" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "That role is a system role, which is governed by the member's base role instead" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No role with that id on this team" + } + }, + "tags": [ + "Team Members" + ] + } + }, + "/teamMembers/{id}/roles/{roleId}": { + "delete": { + "description": "Unassign a custom role from a team member", + "operationId": "deleteTeamMembersByIdRolesByRoleId", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "roleId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The custom role was unassigned" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Team Members" + ] + } + }, + "/teams": { + "get": { + "description": "Get all teams", + "operationId": "getTeams", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Team" + }, + "type": "array" + } + } + }, + "description": "Teams the caller belongs to" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Teams" + ] + }, + "post": { + "description": "Create a new team", + "operationId": "postTeams", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "avatar": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Team" + } + } + }, + "description": "The created team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Teams" + ] + } + }, + "/teams/{id}": { + "delete": { + "description": "Delete a team", + "operationId": "deleteTeamsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The team was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Teams" + ] + }, + "put": { + "description": "Update a team", + "operationId": "putTeamsById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "avatar": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Team" + } + } + }, + "description": "The updated team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Teams" + ] + } + }, + "/templateFolders": { + "get": { + "description": "Get all template folders", + "operationId": "getTemplateFolders", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TemplateFolder" + }, + "type": "array" + } + } + }, + "description": "Template folders in scope" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Template Folders" + ] + }, + "post": { + "description": "Create a new template folder", + "operationId": "postTemplateFolders", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdBy": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isDatabase": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name", + "createdBy", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TemplateFolder" + } + } + }, + "description": "The created folder" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Template Folders" + ] + } + }, + "/templateFolders/{id}": { + "delete": { + "description": "Delete a template folder", + "operationId": "deleteTemplateFoldersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The folder was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Template Folders" + ] + }, + "put": { + "description": "Update a template folder", + "operationId": "putTemplateFoldersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdBy": { + "type": "string" + }, + "id": { + "type": "string" + }, + "isDatabase": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "teamId": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TemplateFolder" + } + } + }, + "description": "The updated folder" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Template Folders" + ] + } + }, + "/templates": { + "get": { + "description": "Get all datamaker templates", + "operationId": "getTemplates", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/Template" + }, + "type": "array" + } + } + }, + "description": "Templates in the caller's team/project scope" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Templates" + ] + }, + "post": { + "description": "Create a new datamaker template", + "operationId": "postTemplates", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdAt": { + "type": "string" + }, + "createdBy": { + "description": "A valid team member id", + "type": "string" + }, + "dbOrderIdx": { + "description": "Not sure if this is needed", + "type": "number" + }, + "fields": { + "default": [], + "items": { + "properties": { + "active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "nested": { + "items": { + "additionalProperties": {}, + "type": "object" + }, + "type": "array" + }, + "options": {}, + "type": { + "type": "string" + } + }, + "required": [ + "name", + "type", + "active" + ], + "type": "object" + }, + "type": "array" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "seed": { + "default": null, + "type": [ + "number", + "null" + ] + }, + "simulationConfig": { + "properties": { + "isSimulationVisible": { + "type": "boolean" + }, + "period": { + "type": "number" + } + }, + "required": [ + "period", + "isSimulationVisible" + ], + "type": "object" + }, + "teamId": { + "type": "string" + }, + "templateFolderId": { + "default": null, + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Template" + } + } + }, + "description": "The created template" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Templates" + ] + } + }, + "/templates/{id}": { + "delete": { + "description": "Delete a template", + "operationId": "deleteTemplatesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The template was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No template with that id in the caller's scope" + } + }, + "tags": [ + "Templates" + ] + }, + "get": { + "description": "Get a datamaker template by id", + "operationId": "getTemplatesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Template" + } + } + }, + "description": "The template" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No template with that id in the caller's scope" + } + }, + "tags": [ + "Templates" + ] + }, + "put": { + "description": "Update a template", + "operationId": "putTemplatesById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "createdAt": { + "type": "string" + }, + "createdBy": { + "description": "A valid team member id", + "type": "string" + }, + "dbOrderIdx": { + "description": "Not sure if this is needed", + "type": "number" + }, + "fields": { + "default": [], + "items": { + "properties": { + "active": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "nested": { + "items": { + "additionalProperties": {}, + "type": "object" + }, + "type": "array" + }, + "options": {}, + "type": { + "type": "string" + } + }, + "required": [ + "name", + "type", + "active" + ], + "type": "object" + }, + "type": "array" + }, + "name": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "seed": { + "default": null, + "type": [ + "number", + "null" + ] + }, + "simulationConfig": { + "properties": { + "isSimulationVisible": { + "type": "boolean" + }, + "period": { + "type": "number" + } + }, + "required": [ + "period", + "isSimulationVisible" + ], + "type": "object" + }, + "teamId": { + "type": "string" + }, + "templateFolderId": { + "default": null, + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "name", + "projectId", + "teamId" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Template" + } + } + }, + "description": "The updated template" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The template has no team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No template with that id in the caller's scope" + } + }, + "tags": [ + "Templates" + ] + } + }, + "/upload": { + "post": { + "description": "Upload an image, PDF, or JSON file to R2 storage", + "operationId": "postUpload", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadResult" + } + } + }, + "description": "Where the file landed and a short-lived URL to fetch it" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No file, an unsupported type, an oversized file, or no determinable extension" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The upload failed" + } + }, + "tags": [ + "Upload" + ] + } + }, + "/upload-csv": { + "post": { + "description": "Upload a CSV file to R2 storage and return a presigned URL", + "operationId": "postUpload-csv", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadResult" + } + } + }, + "description": "The stored key and a presigned download URL" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No file in the form data, a file that is not a CSV, or one over 10MB" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The upload to blob storage failed" + } + }, + "tags": [ + "Upload" + ] + } + }, + "/upload-csv-batch": { + "post": { + "description": "Upload multiple CSV files (e.g. a folder) to R2 with content-hash deduplication. Re-uploading identical content is a no-op.", + "operationId": "postUpload-csv-batch", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsvBatchUploadResult" + } + } + }, + "description": "One entry per file, with `deduped` true where identical content was already stored" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsvBatchUploadError" + } + } + }, + "description": "Malformed JSON body, no files, or more than 30 files" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsvBatchUploadError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsvBatchUploadError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "413": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsvBatchUploadError" + } + } + }, + "description": "A single file is over 10MB, or the batch is over 50MB in total" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CsvBatchUploadError" + } + } + }, + "description": "R2_BUCKET_NAME is unset, or the upload failed" + } + }, + "tags": [ + "Upload" + ] + } + }, + "/upload-text": { + "post": { + "description": "Upload text content to R2 storage. When chatId is provided, also registers the upload as a chat asset in the same request so the S3 blob and the ChatAsset row stay in sync.", + "operationId": "postUpload-text", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "chatId": { + "type": "string" + }, + "filename": { + "minLength": 1, + "type": "string" + }, + "mimeType": { + "type": "string" + }, + "source": { + "enum": [ + "agent", + "upload" + ], + "type": "string" + }, + "text": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "text", + "filename" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UploadTextResult" + } + } + }, + "description": "The stored key and a presigned URL, plus the chat asset id when a chatId was given" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "A chatId was supplied but no chat with that id exists" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The upload to blob storage failed" + } + }, + "tags": [ + "Upload" + ] + } + }, + "/users": { + "get": { + "description": "Get all users", + "operationId": "getUsers", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array" + } + } + }, + "description": "The caller's own user record. Credential columns are omitted client-wide (#2898)" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + }, + "post": { + "description": "Create a new user", + "operationId": "postUsers", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "autoSave": { + "type": "boolean" + }, + "avatar": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "email": { + "format": "email", + "type": "string" + }, + "firstName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "lastName": { + "type": "string" + } + }, + "required": [ + "id", + "email", + "firstName", + "lastName" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "The created user" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + } + }, + "/users/logout": { + "post": { + "description": "Logout user and clear authentication cookie", + "operationId": "postUsersLogout", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The auth cookie was cleared" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + } + }, + "/users/me": { + "get": { + "description": "Get the current user", + "operationId": "getUsersMe", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CurrentUser" + } + } + }, + "description": "The caller's identity from the auth scope, with avatar filled from the token's picture claim" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + } + }, + "/users/me/permissions": { + "get": { + "description": "Get the current user's effective permissions for a team", + "operationId": "getUsersMePermissions", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EffectivePermissions" + } + } + }, + "description": "Effective permissions for the requested or default team" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + } + }, + "/users/me/preferences": { + "get": { + "description": "Get the current user's UI preferences", + "operationId": "getUsersMePreferences", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPreferences" + } + } + }, + "description": "The caller's UI preferences; theme is null when unset" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + }, + "patch": { + "description": "Update the current user's UI preferences", + "operationId": "patchUsersMePreferences", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferencesUpdateResult" + } + } + }, + "description": "The stored theme" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferencesUpdateError" + } + } + }, + "description": "No theme in the body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferencesUpdateError" + } + } + }, + "description": "No user id on the authenticated scope (this body is a bare `{ ok: false }`)" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferencesUpdateError" + } + } + }, + "description": "The caller lacks the required permission" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferencesUpdateError" + } + } + }, + "description": "The write failed" + } + }, + "tags": [ + "Users" + ] + } + }, + "/users/provision": { + "post": { + "description": "Handle user account provisioning", + "operationId": "postUsersProvision", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "Provisioning outcome as a message; the status carries the meaning" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "No user information on the request" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a user-scoped API key, or the account was recently deleted" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Re-provisioning failed" + } + }, + "tags": [ + "Users" + ] + } + }, + "/users/{id}": { + "delete": { + "description": "Delete a user", + "operationId": "deleteUsersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletedResult" + } + } + }, + "description": "The user was deleted" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + }, + "patch": { + "description": "Partially update a user", + "operationId": "patchUsersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "The updated user" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + }, + "put": { + "description": "Update a user", + "operationId": "putUsersById", + "parameters": [ + { + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "autoSave": { + "type": "boolean" + }, + "avatar": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "email": { + "format": "email", + "type": "string" + }, + "firstName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "lastName": { + "type": "string" + } + }, + "required": [ + "id", + "email", + "firstName", + "lastName" + ], + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + }, + "description": "The updated user" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Users" + ] + } + }, + "/validate/apiKey": { + "get": { + "description": "Test API key authentication", + "operationId": "getValidateApiKey", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiKeyValidation" + } + } + }, + "description": "The key authenticated. Only `message` is returned - no key details" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "The caller lacks the required permission" + } + }, + "tags": [ + "Validate" + ] + } + }, + "/workspace-files/by-key": { + "get": { + "description": "Get a workspace file by its storage key with presigned URL", + "operationId": "getWorkspace-filesBy-key", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ScenarioFile" + } + } + }, + "description": "The file for that storage key, with a freshly signed URL" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Missing the key query parameter" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not authenticated" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "Not a member of the scenario's team" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiError" + } + } + }, + "description": "File not found" + } + }, + "tags": [ + "Workspace Files" + ] + } + } + } +} diff --git a/src/datamaker/generated/__init__.py b/src/datamaker/generated/__init__.py new file mode 100644 index 0000000..615183b --- /dev/null +++ b/src/datamaker/generated/__init__.py @@ -0,0 +1,7 @@ +"""Types generated from the API's OpenAPI document. Do not edit by hand. + +Regenerate with `python scripts/generate_schema.py`; CI fails if this drifts +from `spec/openapi.json`. +""" + +from .schema import * # noqa: F401,F403 diff --git a/src/datamaker/generated/schema.py b/src/datamaker/generated/schema.py new file mode 100644 index 0000000..e473179 --- /dev/null +++ b/src/datamaker/generated/schema.py @@ -0,0 +1,1655 @@ +# generated by datamodel-codegen: +# filename: openapi.json + +from __future__ import annotations + +from typing import Any, Literal, NotRequired, TypeAlias, TypedDict + + +class AddressAvailability(TypedDict): + available: bool + + +class ApiError(TypedDict): + code: NotRequired[str] + details: NotRequired[str] + error: str + + +class ApiKey(TypedDict): + createdAt: str + id: str + key: str + name: str + projectId: str | None + role: Literal['READ', 'READ_WRITE', 'SCENARIO'] | str + teamId: str | None + updatedAt: str + userId: str | None + + +class ApiKeyValidation(TypedDict): + message: str + + +class ApiMessageError(TypedDict): + error: NotRequired[str] + message: str + + +class ApprovalDecision(TypedDict): + created: NotRequired[bool] + decision: Literal['allow', 'pending'] + id: str + + +class ApprovalStatus(TypedDict): + id: str + status: str + + +class AuditEvent(TypedDict): + category: Literal['READ', 'WRITE', 'DESTRUCTIVE', 'EXEC'] + chatId: str | None + createdAt: str + decision: Literal['AUTO', 'ALLOWED', 'DENIED', 'APPROVED'] + durationMs: int | None + error: str | None + finishedAt: str | None + id: str + inputDigest: NotRequired[None] + principal: str | None + projectId: str | None + sdkSessionId: str | None + startedAt: str + status: Literal['ok', 'error'] + targetKind: str | None + targetRef: str | None + teamId: str | None + tool: str + trackingId: str | None + + +class AuditEventRef(TypedDict): + id: str + + +class BuiltinSkill(TypedDict): + appliedCount: int + by: str + description: str + enabled: bool + id: str + name: str + scope: Literal['builtin'] + slug: str + + +class CapabilityCatalogItem(TypedDict): + group: NotRequired[str] + hidden: Literal[False] + key: str + name: str + note: str + source: Literal['derived'] + template: str + templateId: str + + +class Chat(TypedDict): + createdAt: str + createdBy: str | None + id: str + messages: NotRequired[Any] + projectId: str | None + title: str + updatedAt: str + + +class ChatAsset(TypedDict): + chatId: str + createdAt: str + filename: str + id: str + mimeType: str | None + presignedUrl: str | None + s3Key: str + size: int | None + source: str + updatedAt: str + + +class ClassifiedField(TypedDict): + name: NotRequired[str] + sensitive: NotRequired[bool] + + +ClassifiedFields: TypeAlias = list[ClassifiedField] + + +class Column(TypedDict): + column_name: str + data_type: str + + +class Dependency(TypedDict): + dependent_column: str + dependent_table: str + referenced_column: str + + +class Row(TypedDict): + total_count: NotRequired[Any] + + +class ConnectionTable(TypedDict): + columns: list[Column] + dependencies: list[Dependency] + rows: list[Row] + table_name: str + + +class ConnectionVerdict(TypedDict): + message: str + ok: bool + status: NotRequired[int] + + +class CsrfToken(TypedDict): + cookie: NotRequired[str] + cookie_name: str + cookie_value: str + csrf_token: str + + +class CsvBatchUploadError(TypedDict): + error: str + ok: Literal[False] + + +class File(TypedDict): + deduped: bool + key: str + name: str + url: str + + +class CsvBatchUploadResult(TypedDict): + files: list[File] + folderName: str | None + folderPrefix: str + ok: Literal[True] + + +class CurrentUser(TypedDict): + avatar: NotRequired[str | None] + email: NotRequired[str] + id: NotRequired[str] + name: NotRequired[str] + picture: NotRequired[str | None] + + +class CustomDataType(TypedDict): + createdBy: str + fieldConfig: NotRequired[Any] + id: str + name: str + projectId: str + teamId: str + + +class Field(TypedDict): + columnID: int + dataTypeID: int + dataTypeModifier: int + dataTypeSize: int + format: str + name: str + tableID: int + + +class DatabaseExportResult(TypedDict): + command: str + fields: list[Field] + oid: int | None + rowCount: int | None + rows: list[Any] + + +class Template(TypedDict): + createdBy: NotRequired[str] + dbOrderIdx: int + fields: list[Any] + name: str + projectId: NotRequired[str] + teamId: NotRequired[str] + templateFolderId: str + + +class DatabaseTemplatesProposal(TypedDict): + ai_prompts: list[dict[str, Any]] + data_generation_order: list[str] | None + templates: list[Template] + + +class DatamakerConfig(TypedDict): + aiGatewayUrl: str + allowedModels: list[str] + apiContractVersion: str + defaultModel: str + opencodeUrl: str + quantityLimit: int + + +class DeletedResult(TypedDict): + message: str + + +class EffectivePermissions(TypedDict): + permissions: list[str] + role: str | None + teamId: str | None + + +class Endpoint(TypedDict): + auth: NotRequired[None] + createdAt: str + createdBy: str | None + endpointFolderId: str | None + headers: NotRequired[None] + id: str + method: Literal['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] + name: str + projectId: str | None + queryParams: NotRequired[None] + teamId: str | None + url: str + + +class EndpointFolder(TypedDict): + createdAt: str + createdBy: str | None + id: str + name: str + projectId: str + teamId: str + + +class Feedback(TypedDict): + comment: str + createdAt: str + createdBy: str | None + feeling: str + id: str + + +class FieldType(TypedDict): + meta: dict[str, Any] + options: dict[str, Any] + type: str + + +class GeneratedData(TypedDict): + dependencies: dict[str, Any] + live_data: dict[str, Any] + + +class GeneratedTemplateField(TypedDict): + active: NotRequired[bool] + function: NotRequired[str] + name: str + nested: NotRequired[list[GeneratedTemplateField]] + optional: NotRequired[Any] + options: NotRequired[Any] + textCase: NotRequired[str] + type: str + + +GeneratedTemplateFields: TypeAlias = list[GeneratedTemplateField] + + +class ImageAnalysisResult(TypedDict): + description: str + success: Literal[True] + + +class Integration(TypedDict): + auth: NotRequired[None] + createdAt: str + createdBy: str | None + endpointFolderId: str | None + headers: NotRequired[None] + id: str + kind: str + name: str + origin: str + projectId: str | None + scope: str | None + teamId: str | None + + +class KeyMapDeleteResult(TypedDict): + deleted: int + message: str + + +class KeyMapEntry(TypedDict): + newKey: str + object: str + oldKey: str + runId: str | None + updatedAt: str + + +class KeyMapLookupResult(TypedDict): + mapName: str + mappings: dict[str, str] + missing: list[str] + object: str + + +class KeyMapSummary(TypedDict): + entryCount: int + mapName: str + object: str + updatedAt: str | None + + +class KeyMapUpsertResult(TypedDict): + mapName: str + object: str + upserted: int + + +class License(TypedDict): + captured: NotRequired[None] + expiresAt: str | None + id: str + issuedAt: str + issuer: str + keyId: str | None + keyMasked: str + offline: bool + revokedAt: str | None + seats: int | None + status: str + supersededById: str | None + teamId: str | None + type: str + + +class LicenseStatus(TypedDict): + code: str | None + graceDays: int | None + graceEndsAt: str | None + issuer: str | None + message: str | None + mode: str + readOnlyReason: str | None + required: bool + + +class LogCleanupResult(TypedDict): + cutoffDate: str + deletedCount: int + message: str + + +class LogoutResult(TypedDict): + ok: Literal[True] + + +class MaskingPolicy(TypedDict): + consistent: bool + createdAt: str + createdBy: str | None + description: str | None + fields: NotRequired[Any] + id: str + keyMapName: str | None + name: str + projectId: str + reversible: bool + teamId: str + updatedAt: str + + +class MemberRoleAssignment(TypedDict): + createdAt: str + id: str + roleId: str + teamMemberId: str + + +class PackCatalog(TypedDict): + enabled: bool + packs: NotRequired[list[Any]] + + +class PackCreatedCounts(TypedDict): + datatypes: int + plans: int + scenarios: int + skills: int + templates: int + + +class Datatype(TypedDict): + fieldConfig: NotRequired[Any] + name: str + + +class Approval(TypedDict): + reason: str + role: str + + +class Capability(TypedDict): + expectedCount: NotRequired[int] + expectedSumCents: NotRequired[int] + group: NotRequired[str] + hidden: bool + key: str + name: str + note: str + setId: NotRequired[str] + source: Literal['derived', 'manual'] + sumField: NotRequired[str] + template: NotRequired[str] + templateId: NotRequired[str] + + +class CoverageItem(TypedDict): + count: int + family: str + items: list[str] + + +class CoverageConfig(TypedDict): + freshnessDays: int + mode: Literal['release', 'migration'] + sourceSystem: NotRequired[str] + sumToleranceCents: int + targetSystem: NotRequired[str] + + +class Entity(TypedDict): + endpointId: NotRequired[str] + key: str + name: str + note: str + template: str + templateId: NotRequired[str] + volume: int + + +class Scaffold(TypedDict): + entity: str + fields: list[str] + ops: list[str] + + +Step = TypedDict( + 'Step', + { + 'fields': NotRequired[list[str]], + 'from': str, + 'iface': str, + 'kind': str, + 'msg': str, + 'reachable': NotRequired[bool], + 'scaffold': NotRequired[Scaffold], + 'status': Literal['mapped', 'likely', 'custom', 'unknown'], + 'to': str, + 'why': NotRequired[str], + }, +) + + +class FlowItem(TypedDict): + loop: NotRequired[str | None] + phase: str + steps: list[Step] + + +class Gap(TypedDict): + action: NotRequired[str] + iface: NotRequired[str] + note: str + scaffold: NotRequired[Scaffold] + title: str + type: Literal['connect', 'custom', 'input', 'blocker', 'gap', 'stale'] + + +class Integration1(TypedDict): + access: Literal['read', 'write'] + detail: str + system: str + via: str + + +class Lifecycle(TypedDict): + mode: str + refresh: str + replenish: str + + +class MappingConfig(TypedDict): + keyMapName: NotRequired[str] + maskingPolicyId: NotRequired[str] + maskingPolicyName: NotRequired[str] + sourceObject: NotRequired[str] + sourceSystem: NotRequired[str] + targetObject: NotRequired[str] + targetSystem: NotRequired[str] + + +class Mapping(TypedDict): + generate: bool + keyMapName: NotRequired[str] + maskingPolicyId: NotRequired[str] + note: str + sourceField: NotRequired[str] + status: Literal['mapped', 'open', 'needs-review'] + targetField: str + transform: str + + +class Task1(TypedDict): + deps: list[str] + detail: str + key: NotRequired[str] + priority: NotRequired[Literal['P0', 'P1', 'P2', 'P3']] + title: str + + +class Task(TypedDict): + note: NotRequired[str] + phase: str + tasks: list[Task1] + + +class Version(TypedDict): + by: str + n: int + when: str + + +class Spec(TypedDict): + approval: NotRequired[Approval] + capabilities: NotRequired[list[Capability]] + constraints: list[str] + coverage: NotRequired[list[CoverageItem]] + coverageConfig: NotRequired[CoverageConfig] + entities: list[Entity] + expectations: list[str] + flow: NotRequired[list[FlowItem]] + gaps: list[Gap] + integrations: list[Integration1] + kind: NotRequired[Literal['entity', 'flow', 'task', 'mapping']] + lanes: list[str] + lifecycle: NotRequired[Lifecycle] + mappingConfig: NotRequired[MappingConfig] + mappings: NotRequired[list[Mapping]] + steps: list[str] + tasks: NotRequired[list[Task]] + version: NotRequired[Version] + + +class Plan(TypedDict): + spec: Spec + summary: NotRequired[str] + title: str + + +class File1(TypedDict): + content: str + name: str + + +class Param(TypedDict): + default: NotRequired[str] + description: NotRequired[str] + name: str + + +class Scenario(TypedDict): + description: NotRequired[str] + files: list[File1] + name: str + params: list[Param] + + +class Skill(TypedDict): + body: str + description: str + name: str + slug: str + + +class Template1(TypedDict): + fields: NotRequired[Any] + name: str + seed: NotRequired[int] + simulationConfig: NotRequired[Any] + + +class Assets(TypedDict): + datatypes: list[Datatype] + plans: list[Plan] + scenarios: list[Scenario] + skills: list[Skill] + templates: list[Template1] + + +class Require(TypedDict): + kind: Literal['integration'] + note: NotRequired[str] + type: str + + +class Manifest(TypedDict): + createdAt: NotRequired[str] + description: str + license: NotRequired[str] + name: str + publisher: str + requires: list[Require] + version: str + + +class Pack(TypedDict): + assets: Assets + checksum: NotRequired[str] + format: Literal['dmpack@1'] + manifest: Manifest + publicKeyId: NotRequired[str] + signature: NotRequired[str] + + +class PackExportResult(TypedDict): + pack: Pack + warnings: list[str] + + +class Entry(TypedDict): + action: Literal['create', 'skip'] + conflict: NotRequired[bool] + key: str + kind: Literal['template', 'skill', 'plan', 'datatype', 'scenario'] + + +class MissingRequirement(TypedDict): + kind: Literal['integration'] + note: NotRequired[str] + type: str + + +class Pack1(TypedDict): + name: str + version: str + + +class PackImportDiff(TypedDict): + containsScenarios: bool + entries: list[Entry] + missingRequirements: list[MissingRequirement] + pack: Pack1 + signature: Literal['valid', 'invalid', 'unsigned'] + + +class Manifest1(TypedDict): + createdAt: NotRequired[str] + description: str + license: NotRequired[str] + name: str + publisher: str + requires: list[Require] + version: str + + +class PackInstall(TypedDict): + created: PackCreatedCounts + createdAt: str + id: str + installedBy: str | None + manifest: Manifest1 + name: str + projectId: str | None + signature: str + source: Literal['catalog', 'file'] + teamId: str | None + version: str + + +class PackInstallListItem(PackInstall): + installedByName: str | None + + +class PackInstallResult(TypedDict): + created: NotRequired[PackCreatedCounts] + diff: PackImportDiff + install: NotRequired[PackInstall] + + +class PdfAnalysisResult(TypedDict): + documentInfo: dict[str, Any] + downloadUrl: str + method: Literal['text_extraction', 'image_analysis'] + success: Literal[True] + textLength: NotRequired[int] + + +class PermissionCatalogue(TypedDict): + all: list[str] + grouped: dict[str, list[str]] + + +class HistoryItem(TypedDict): + by: NotRequired[str] + note: str + status: Literal['draft', 'approved', 'running', 'completed', 'failed'] + when: str + + +Step1 = TypedDict( + 'Step1', + { + 'fields': NotRequired[list[str]], + 'from': str, + 'iface': str, + 'kind': str, + 'msg': str, + 'reachable': NotRequired[bool], + 'scaffold': NotRequired[Scaffold], + 'status': Literal['mapped', 'likely', 'custom', 'unknown'], + 'to': str, + 'why': NotRequired[str], + }, +) + + +class FlowItem1(TypedDict): + loop: NotRequired[str | None] + phase: str + steps: list[Step1] + + +class Gap1(TypedDict): + action: NotRequired[str] + iface: NotRequired[str] + note: str + scaffold: NotRequired[Scaffold] + title: str + type: Literal['connect', 'custom', 'input', 'blocker', 'gap', 'stale'] + + +class Task3(TypedDict): + deps: list[str] + detail: str + key: NotRequired[str] + priority: NotRequired[Literal['P0', 'P1', 'P2', 'P3']] + title: str + + +class Task2(TypedDict): + note: NotRequired[str] + phase: str + tasks: list[Task3] + + +class Spec1(TypedDict): + approval: NotRequired[Approval] + capabilities: NotRequired[list[Capability]] + constraints: list[str] + coverage: NotRequired[list[CoverageItem]] + coverageConfig: NotRequired[CoverageConfig] + entities: list[Entity] + expectations: list[str] + flow: NotRequired[list[FlowItem1]] + gaps: list[Gap1] + integrations: list[Integration1] + kind: NotRequired[Literal['entity', 'flow', 'task', 'mapping']] + lanes: list[str] + lifecycle: NotRequired[Lifecycle] + mappingConfig: NotRequired[MappingConfig] + mappings: NotRequired[list[Mapping]] + steps: list[str] + tasks: NotRequired[list[Task2]] + version: NotRequired[Version] + + +class Plan1(TypedDict): + createdAt: str + createdBy: str | None + createdFrom: str | None + env: str | None + history: list[HistoryItem] + id: str + origin: Literal['chat', 'blueprint', 'intake', 'gap analysis'] | None + owner: str | None + projectId: str + rows: int + spec: Spec1 + status: Literal['draft', 'approved', 'running', 'completed', 'failed'] + summary: str | None + targets: list[str] + teamId: str + title: str + updatedAt: str + write: bool + + +class PlanDeleteResult(TypedDict): + success: bool + + +class Config(TypedDict): + freshnessDays: int + mode: Literal['release', 'migration'] + sourceSystem: NotRequired[str] + sumToleranceCents: int + targetSystem: NotRequired[str] + + +class Gap2(TypedDict): + action: NotRequired[str] + iface: NotRequired[str] + note: str + scaffold: NotRequired[Scaffold] + title: str + type: Literal['connect', 'custom', 'input', 'blocker', 'gap', 'stale'] + + +class Check(TypedDict): + detail: str + id: str + label: str + status: Literal['pass', 'warn', 'fail', 'skip'] + + +class Item(TypedDict): + checks: list[Check] + group: NotRequired[str] + key: str + name: str + status: Literal['ready', 'partial', 'missing', 'reconciled', 'discrepancy'] + + +class Summary(TypedDict): + failing: int + partial: int + passing: int + scorePct: int + total: int + + +class Results(TypedDict): + config: Config + gaps: list[Gap2] + items: list[Item] + mode: Literal['release', 'migration'] + summary: Summary + verdict: Literal['ready', 'partial', 'blocked', 'reconciled', 'discrepancy'] + + +class PlanRunAccepted(TypedDict): + runId: str + status: Literal['running'] + + +class PlanRunAck(TypedDict): + ok: Literal[True] + + +class PlanRunFile(TypedDict): + entity: str + filename: str + id: str + mimeType: str | None + presignedUrl: str | None + size: int + + +class File2(TypedDict): + entity: str + filename: str + id: str + key: str + mimeType: str | None + size: int + + +class PlanRunFileAck(TypedDict): + file: File2 + ok: Literal[True] + + +class PlanSignoff(TypedDict): + createdAt: str + id: str + note: str | None + planId: str + planRunId: str + report: NotRequired[Any] + reportHash: str + signedBy: str + verdict: str + + +class PreferencesUpdateError(TypedDict): + error: NotRequired[str] + ok: Literal[False] + + +class PreferencesUpdateResult(TypedDict): + ok: Literal[True] + theme: str + + +class PreviewResult(TypedDict): + data: NotRequired[Any] + headers: dict[str, str] + isJson: bool + responseTime: int + status: int + statusText: str + + +class Project(TypedDict): + avatar: str | None + createdAt: str + createdBy: str + description: str | None + id: str + name: str + teamId: str + + +class PythonExecutionError(TypedDict): + error: str + executionTime: NotRequired[float] + jobId: NotRequired[str] + logs: NotRequired[list[Any]] + result: NotRequired[Any] + status: NotRequired[str] + success: Literal[False] + + +class PythonExecutionResult(TypedDict): + executionTime: NotRequired[float] + jobId: str + logs: NotRequired[list[Any]] + message: NotRequired[str] + result: NotRequired[Any] + status: str + success: Literal[True] + + +class Role(TypedDict): + baseRole: str | None + createdAt: str + description: str | None + id: str + isSystem: bool + name: str + teamId: str + updatedAt: str + + +class Service(TypedDict): + description: str + id: str + isActive: NotRequired[bool] + metadataUrl: str + serviceUrl: str + version: NotRequired[str] + + +class SapServiceCatalog(TypedDict): + activeCount: NotRequired[int] + inactiveCount: NotRequired[int] + probed: bool + probedCount: NotRequired[int] + services: list[Service] + status: NotRequired[Literal['active', 'inactive']] + total: int + truncated: NotRequired[bool] + + +class Scenario1(TypedDict): + createdAt: str + createdBy: str | None + description: str | None + diagram: str | None + diagramBusiness: str | None + diagramError: str | None + diagramSourceHash: str | None + diagramStatus: str + diagramUpdatedAt: str | None + environmentVariables: NotRequired[Any] + id: str + name: str + presignedUrl: str | None + projectId: str + requirementsUrl: str | None + storageUsed: int + teamId: str + timeoutSeconds: int + updatedAt: str + + +class ScenarioDiagramRegenerateAccepted(TypedDict): + status: Literal['pending'] + + +class EnvironmentVariable(TypedDict): + key: str + value: str + + +class ScenarioEnvironmentVariableList(TypedDict): + environmentVariables: list[EnvironmentVariable] + + +class ScenarioEnvironmentVariables(TypedDict): + environmentVariables: dict[str, str] + + +class ScenarioFile(TypedDict): + createdAt: str + createdBy: str | None + filename: str + folder: Literal['uploads', 'outputs'] + id: str + key: str + mimeType: str | None + presignedUrl: str | None + scenarioId: str + size: int + updatedAt: str + + +class ScenarioFileCreated(TypedDict): + createdAt: str + filePath: str + folder: str + id: str + mimeType: str | None + name: str + size: int + updatedAt: str + + +class ScenarioJob(TypedDict): + createdAt: float | None + finishedAt: float | None + jobId: str | None + processedAt: float | None + projectId: str | None + scenarioId: str | None + scenarioName: str | None + state: str + teamId: str | None + + +class Logs(TypedDict): + count: int + logs: list[str] + + +class ScenarioJobStatus(TypedDict): + error: str | None + logs: Logs + output: str | None + progress: float | str | bool | dict[str, Any] + state: str + + +class ScenarioJobs(TypedDict): + jobs: list[ScenarioJob] + + +class ScenarioLog(TypedDict): + completedAt: str | None + createdBy: str + error: str | None + id: str + jobId: str + logs: list[Any] + output: str | None + scenarioId: str + source: str + startedAt: str + status: str + + +class Creatable(TypedDict): + cached: bool + live: bool + + +class EntitySet(TypedDict): + cached: NotRequired[str] + live: NotRequired[str] + + +class Keys(TypedDict): + cached: list[str] + live: list[str] + + +class Label(TypedDict): + cached: str + live: str + + +class Label1(TypedDict): + cached: NotRequired[str] + live: NotRequired[str] + + +class MaxLength(TypedDict): + cached: NotRequired[int] + live: NotRequired[int] + + +class Nullable(TypedDict): + cached: bool + live: bool + + +class Type(TypedDict): + cached: str + live: str + + +class Updatable(TypedDict): + cached: bool + live: bool + + +class Changes(TypedDict): + creatable: NotRequired[Creatable] + label: NotRequired[Label1] + maxLength: NotRequired[MaxLength] + nullable: NotRequired[Nullable] + type: NotRequired[Type] + updatable: NotRequired[Updatable] + + +class Properties1(TypedDict): + changes: Changes + kind: Literal['property'] + name: str + status: Literal['modified'] + + +class Cardinality(TypedDict): + cached: str + live: str + + +class To(TypedDict): + cached: str + live: str + + +class Changes1(TypedDict): + cardinality: NotRequired[Cardinality] + to: NotRequired[To] + + +SchemaGraphChange4 = TypedDict( + 'SchemaGraphChange4', + { + 'changes': Changes1, + 'from': str, + 'kind': Literal['navigation'], + 'name': str, + 'status': Literal['modified'], + }, +) + + +class SchemaGraphConnectResult(TypedDict): + endpointId: str + endpointName: str + entity: str + fieldsUpgraded: int + templateId: str + upgraded: list[str] + + +class Namespace(TypedDict): + cached: str + live: str + + +class OdataVersion(TypedDict): + cached: str + live: str + + +class Summary1(TypedDict): + entitiesAdded: int + entitiesModified: int + entitiesRemoved: int + navigationsAdded: int + navigationsChanged: int + navigationsRemoved: int + propertiesAdded: int + propertiesChanged: int + propertiesRemoved: int + + +NavigatedFromItem = TypedDict( + 'NavigatedFromItem', + { + 'cardinality': str, + 'from': str, + 'name': str, + }, +) + + +class NavigatesToItem(TypedDict): + cardinality: str + name: str + to: str + + +class SchemaGraphMinedField(TypedDict): + counts: list[int] + distinct: int + enumLike: bool + name: str + sampled: int + values: list[str] + + +SchemaGraphNavigation = TypedDict( + 'SchemaGraphNavigation', + { + 'cardinality': str, + 'from': str, + 'kind': Literal['navigation'], + 'name': str, + 'to': str, + }, +) + + +PathItem = TypedDict( + 'PathItem', + { + 'from': str, + 'to': str, + 'via': str, + }, +) + + +class SchemaGraphPathResult(TypedDict): + path: list[PathItem] + + +class SchemaGraphPreviewResult(TypedDict): + count: int + entity: str + entitySet: str + rows: list[dict[str, Any]] + + +class SchemaGraphProperty(TypedDict): + creatable: bool + label: NotRequired[str] + maxLength: NotRequired[int] + name: str + nullable: bool + type: str + updatable: bool + + +class SchemaGraphRefreshResult(TypedDict): + entities: int + metadataUrl: str + namespace: str + navigations: int + odataVersion: str + schemaChanged: bool + + +class SchemaGraphSampleResult(TypedDict): + entity: str + fields: list[SchemaGraphMinedField] + rowsSampled: int + valueHelpSets: list[str] + + +class Result(TypedDict): + entitySet: NotRequired[str] + label: str + name: str + score: int + + +class SchemaGraphSearchResult(TypedDict): + results: list[Result] + + +class SchemaGraphTemplateResult(TypedDict): + entity: str + fieldCount: int + id: str + minedFields: list[str] + name: str + + +class SessionTokens(TypedDict): + accessToken: str + expiresIn: int + refreshExpiresAt: str + refreshToken: str + + +class Set(TypedDict): + createdAt: str + createdBy: str | None + data: NotRequired[Any] + description: str | None + id: str + locked: bool + name: str + projectId: str + rowCount: int + teamId: str + updatedAt: str + + +class SetDetail(Set): + createdByName: str | None + + +class Shortcut(TypedDict): + context: str + createdAt: str + function: str + id: str + keys: list[str] + updatedAt: str + userId: str | None + + +StringList: TypeAlias = list[str] + + +class SuccessResult(TypedDict): + success: bool + + +class Team(TypedDict): + avatar: str | None + createdAt: str + id: str + name: str + updatedAt: str + + +class TeamMember(TypedDict): + id: str + role: str + teamId: str + userId: str + + +class TeamSetupResult(TypedDict): + project: Project + team: Team + teamMember: TeamMember + + +class TeamSkill(TypedDict): + appliedCount: int + body: str + createdAt: str + createdBy: str | None + description: str + enabled: bool + id: str + name: str + scope: Literal['team'] + slug: str + updatedAt: str + + +class Template2(TypedDict): + createdAt: str + createdBy: str | None + dbOrderIdx: int + fields: NotRequired[Any] + id: str + name: str + projectId: str | None + seed: int | None + simulationConfig: NotRequired[None] + teamId: str | None + templateFolderId: str | None + + +class TemplateFolder(TypedDict): + createdAt: str + createdBy: str | None + id: str + isDatabase: bool + name: str + projectId: str | None + teamId: str | None + + +class ToscaOnPremWorkspaces(TypedDict): + workspaces: list[str] + + +class ToscaWorkspaces(TypedDict): + kind: Literal['tosca-onprem', 'tosca-cloud'] + workspaces: list[str] + + +class UploadResult(TypedDict): + downloadUrl: str + fileName: str + success: bool + + +class UploadTextResult(TypedDict): + chatAssetId: NotRequired[str] + fileName: str + success: Literal[True] + url: str + + +class User(TypedDict): + autoSave: bool | None + avatar: str | None + createdAt: str + creationPurpose: str + email: str + firstName: str + id: str + lastLogin: str + lastName: str + + +class UserPreferences(TypedDict): + theme: str | None + + +class Paths(TypedDict): + outputs: str + uploads: str + + +class WorkerWorkspaceInfo(TypedDict): + fileCount: int + maxFileSize: int + paths: Paths + projectId: str + scenarioId: str + storageLimit: int + storageUsed: int + teamId: str + + +class File3(TypedDict): + downloadUrl: str | None + filename: str + folder: str + id: str + key: str + mimeType: str | None + size: int + + +class WorkerWorkspaceListing(TypedDict): + files: list[File3] + scenarioId: str + + +class Results1(TypedDict): + errors: list[str] + synced: int + + +class WorkerWorkspaceSyncResult(TypedDict): + results: Results1 + success: Literal[True] + + +class File4(TypedDict): + filename: str + id: str + key: str + size: int + + +class WorkerWorkspaceUploadResult(TypedDict): + file: File4 + success: Literal[True] + + +class WorkspaceListing(TypedDict): + files: list[ScenarioFile] + storageLimit: int + storageUsed: int + + +class WorkspaceStorage(TypedDict): + storageLimit: int + storageUsed: int + storageUsedPercent: int + + +class WorkspaceUploadResult(TypedDict): + file: ScenarioFile + success: bool + + +class KeyMapEntriesPage(TypedDict): + entries: list[KeyMapEntry] + mapName: str + page: int + pageSize: int + total: int + + +class NewMember(TeamMember): + firstName: str | None + lastName: str | None + + +class MemberInviteResult(TypedDict): + newMember: NewMember + success: Literal[True] + + +class PlanRun(TypedDict): + completedAt: str | None + error: str | None + files: NotRequired[list[PlanRunFile]] + id: str + logs: list[Any] + mode: str + planId: str + results: Results | None + rows: int + source: str + startedAt: str + status: str + verdict: str | None + + +class Properties(TypedDict): + kind: Literal['property'] + property: SchemaGraphProperty + status: Literal['added', 'removed'] + + +class SchemaGraphChange2(TypedDict): + creatable: NotRequired[Creatable] + entitySet: NotRequired[EntitySet] + keys: NotRequired[Keys] + kind: Literal['entity'] + label: NotRequired[Label] + name: str + properties: list[Properties | Properties1] + status: Literal['modified'] + updatable: NotRequired[Updatable] + + +class SchemaGraphChange3(TypedDict): + kind: Literal['navigation'] + navigation: SchemaGraphNavigation + status: Literal['added', 'removed'] + + +class SchemaGraphEntity(TypedDict): + creatable: bool + entitySet: NotRequired[str] + keys: list[str] + kind: Literal['entity'] + label: str + name: str + properties: list[SchemaGraphProperty] + updatable: bool + + +class SchemaGraphEntityContext(TypedDict): + entity: SchemaGraphEntity + navigatedFrom: list[NavigatedFromItem] + navigatesTo: list[NavigatesToItem] + + +Skill1: TypeAlias = TeamSkill | BuiltinSkill + + +class SchemaGraphChange1(TypedDict): + entity: SchemaGraphEntity + kind: Literal['entity'] + status: Literal['added', 'removed'] + + +SchemaGraphChange: TypeAlias = ( + SchemaGraphChange1 | SchemaGraphChange2 | SchemaGraphChange3 | SchemaGraphChange4 +) + + +class SchemaGraphDiffResult(TypedDict): + cachedRefreshedAt: str + changes: list[SchemaGraphChange] + drifted: bool + endpointId: str + metadataUrl: str + namespace: Namespace + odataVersion: OdataVersion + summary: Summary1 diff --git a/src/datamaker/main.py b/src/datamaker/main.py index 1664367..cad4a67 100644 --- a/src/datamaker/main.py +++ b/src/datamaker/main.py @@ -1,6 +1,7 @@ import os from dotenv import load_dotenv -from typing import Optional, Dict, List +from typing import Any, Optional, Dict, List +from .types import WorkspaceUploadResult from .routes.base import BaseClient from .routes.generation import GenerationClient from .routes.templates import TemplatesClient @@ -95,11 +96,13 @@ def generate_from_template_id(self, template_id: str, quantity: int = 10): # Get the template template = self._templates.get_template_by_id(template_id) - # Set the quantity - template["quantity"] = quantity + # Copy rather than mutate: `quantity` is a generation parameter the + # request carries, not a field of the stored Template, and writing it + # onto the fetched row would put a key there that the API never sends. + payload: Dict[str, Any] = {**template, "quantity": quantity} # Generate data using the template - return self.generate(template) + return self.generate(payload) # =================== TEMPLATE METHODS =================== def get_templates(self): @@ -629,7 +632,7 @@ def save_file( description: Optional[str] = None, folder_id: Optional[str] = None, folder: str = "uploads", - ) -> Dict: + ) -> WorkspaceUploadResult: """Convenience method to save a local file to workspace storage. This method simplifies uploading files by automatically pulling required diff --git a/src/datamaker/routes/api_keys.py b/src/datamaker/routes/api_keys.py index 455146f..7b85cf4 100644 --- a/src/datamaker/routes/api_keys.py +++ b/src/datamaker/routes/api_keys.py @@ -1,11 +1,12 @@ from .base import BaseClient from typing import Optional, Dict, List +from ..types import ApiKey, DeletedResult class ApiKeysClient(BaseClient): """Client for API key management operations.""" - def get_api_keys(self) -> List[Dict]: + def get_api_keys(self) -> List[ApiKey]: """Get all API keys.""" response = self._make_request("GET", "/apiKeys") return response.json() @@ -16,7 +17,7 @@ def create_api_key( scope: str, name: Optional[str] = None, team_id: Optional[str] = None, - ) -> Dict: + ) -> ApiKey: """Create a new API key.""" data = {"key": key, "scope": scope} if name: @@ -33,7 +34,7 @@ def update_api_key( key: str, name: Optional[str] = None, team_id: Optional[str] = None, - ) -> Dict: + ) -> ApiKey: """Update an API key.""" data = {"key": key} if name: @@ -44,7 +45,7 @@ def update_api_key( response = self._make_request("PUT", f"/apiKeys/{key_id}", json=data) return response.json() - def delete_api_key(self, key_id: str) -> Dict: + def delete_api_key(self, key_id: str) -> DeletedResult: """Delete an API key.""" response = self._make_request("DELETE", f"/apiKeys/{key_id}") return response.json() diff --git a/src/datamaker/routes/connections.py b/src/datamaker/routes/connections.py index 7ef8304..a15a431 100644 --- a/src/datamaker/routes/connections.py +++ b/src/datamaker/routes/connections.py @@ -1,5 +1,6 @@ from .base import BaseClient -from typing import Optional, Dict, List, Literal +from typing import Any, Dict, List, Literal, Optional +from ..types import ConnectionTable class ConnectionsClient(BaseClient): @@ -24,7 +25,7 @@ def create_connection( endpoint_folder_id: Optional[str] = None, ) -> Dict: """Create a new database connection.""" - data = { + data: Dict[str, Any] = { "name": name, "type": connection_type, "connectionString": connection_string, @@ -55,7 +56,7 @@ def update_connection( endpoint_folder_id: Optional[str] = None, ) -> Dict: """Update a database connection.""" - data = { + data: Dict[str, Any] = { "name": name, "type": connection_type, "connectionString": connection_string, @@ -81,7 +82,7 @@ def test_connection(self, connection_data: Dict) -> Dict: response = self._make_request("POST", "/connections/test", json=connection_data) return response.json() - def get_tables(self) -> List[Dict]: + def get_tables(self) -> List[ConnectionTable]: """Get all tables from connections.""" response = self._make_request("GET", "/connections/tables") return response.json() diff --git a/src/datamaker/routes/custom_types.py b/src/datamaker/routes/custom_types.py index 684d72f..4e8b584 100644 --- a/src/datamaker/routes/custom_types.py +++ b/src/datamaker/routes/custom_types.py @@ -1,29 +1,30 @@ from .base import BaseClient from typing import Optional, Dict, List +from ..types import CustomDataType, DeletedResult, Endpoint, EndpointFolder class CustomDataTypesClient(BaseClient): """Client for custom data type management operations.""" - def get_custom_data_types(self, project_id: str) -> List[Dict]: + def get_custom_data_types(self, project_id: str) -> List[CustomDataType]: """Get all custom data types for a specific project.""" params = {"projectId": project_id} response = self._make_request("GET", "/customDataTypes", params=params) return response.json() - def create_custom_data_type(self, data_type_data: Dict) -> Dict: + def create_custom_data_type(self, data_type_data: Dict) -> CustomDataType: """Create a new custom data type.""" response = self._make_request("POST", "/customDataTypes", json=data_type_data) return response.json() - def update_custom_data_type(self, data_type_id: str, data_type_data: Dict) -> Dict: + def update_custom_data_type(self, data_type_id: str, data_type_data: Dict) -> CustomDataType: """Update a custom data type.""" response = self._make_request( "PUT", f"/customDataTypes/{data_type_id}", json=data_type_data ) return response.json() - def delete_custom_data_type(self, data_type_id: str) -> Dict: + def delete_custom_data_type(self, data_type_id: str) -> DeletedResult: """Delete a custom data type.""" response = self._make_request("DELETE", f"/customDataTypes/{data_type_id}") return response.json() @@ -32,25 +33,30 @@ def delete_custom_data_type(self, data_type_id: str) -> Dict: class EndpointFoldersClient(BaseClient): """Client for endpoint folder management operations.""" - def get_endpoint_folders(self) -> List[Dict]: + def get_endpoint_folders(self) -> List[EndpointFolder]: """Get all endpoint folders.""" response = self._make_request("GET", "/endpointFolders") return response.json() - def create_endpoint_folder(self, folder_data: Dict) -> Dict: + def create_endpoint_folder(self, folder_data: Dict) -> EndpointFolder: """Create a new endpoint folder.""" response = self._make_request("POST", "/endpointFolders", json=folder_data) return response.json() - def update_endpoint_folder(self, folder_id: str, folder_data: Dict) -> Dict: + def update_endpoint_folder(self, folder_id: str, folder_data: Dict) -> EndpointFolder: """Update an endpoint folder.""" response = self._make_request( "PUT", f"/endpointFolders/{folder_id}", json=folder_data ) return response.json() - def delete_endpoint_folder(self, folder_id: str) -> Dict: - """Delete an endpoint folder.""" + def delete_endpoint_folder(self, folder_id: str) -> EndpointFolder: + """Delete an endpoint folder. + + Returns the DELETED ROW, not the `{message}` its neighbours return. + Taken from the API's response schema rather than assumed from the + verb - `delete_endpoint` next door really does return DeletedResult. + """ response = self._make_request("DELETE", f"/endpointFolders/{folder_id}") return response.json() @@ -58,29 +64,29 @@ def delete_endpoint_folder(self, folder_id: str) -> Dict: class EndpointsClient(BaseClient): """Client for endpoint management operations.""" - def get_endpoints(self) -> List[Dict]: + def get_endpoints(self) -> List[Endpoint]: """Get all endpoints.""" response = self._make_request("GET", "/endpoints") return response.json() - def create_endpoint(self, endpoint_data: Dict) -> Dict: + def create_endpoint(self, endpoint_data: Dict) -> Endpoint: """Create a new endpoint.""" response = self._make_request("POST", "/endpoints", json=endpoint_data) return response.json() - def get_endpoint(self, endpoint_id: str) -> Dict: + def get_endpoint(self, endpoint_id: str) -> Endpoint: """Get a specific endpoint by ID.""" response = self._make_request("GET", f"/endpoints/{endpoint_id}") return response.json() - def update_endpoint(self, endpoint_id: str, endpoint_data: Dict) -> Dict: + def update_endpoint(self, endpoint_id: str, endpoint_data: Dict) -> Endpoint: """Update an endpoint.""" response = self._make_request( "PUT", f"/endpoints/{endpoint_id}", json=endpoint_data ) return response.json() - def delete_endpoint(self, endpoint_id: str) -> Dict: + def delete_endpoint(self, endpoint_id: str) -> DeletedResult: """Delete an endpoint.""" response = self._make_request("DELETE", f"/endpoints/{endpoint_id}") return response.json() diff --git a/src/datamaker/routes/export_and_validation.py b/src/datamaker/routes/export_and_validation.py index 79a633b..d235b7d 100644 --- a/src/datamaker/routes/export_and_validation.py +++ b/src/datamaker/routes/export_and_validation.py @@ -1,5 +1,6 @@ from .base import BaseClient from typing import Dict +from ..types import ApiKeyValidation, DatabaseExportResult class ExportClient(BaseClient): @@ -10,7 +11,7 @@ def export_to_rest(self, export_data: Dict) -> Dict: response = self._make_request("POST", "/export/rest", json=export_data) return response.json() - def export_to_database(self, export_data: Dict) -> Dict: + def export_to_database(self, export_data: Dict) -> DatabaseExportResult: """Export data to database.""" response = self._make_request("POST", "/export/db", json=export_data) return response.json() @@ -19,7 +20,7 @@ def export_to_database(self, export_data: Dict) -> Dict: class ValidationClient(BaseClient): """Client for validation operations.""" - def validate_api_key(self) -> Dict: + def validate_api_key(self) -> ApiKeyValidation: """Test API key authentication.""" response = self._make_request("GET", "/validate/apiKey") return response.json() diff --git a/src/datamaker/routes/folders_and_utils.py b/src/datamaker/routes/folders_and_utils.py index cce59b9..a866e85 100644 --- a/src/datamaker/routes/folders_and_utils.py +++ b/src/datamaker/routes/folders_and_utils.py @@ -1,28 +1,29 @@ from .base import BaseClient from typing import Optional, Dict, List +from ..types import DeletedResult, Feedback, Shortcut, TemplateFolder class TemplateFoldersClient(BaseClient): """Client for template folder management operations.""" - def get_template_folders(self) -> List[Dict]: + def get_template_folders(self) -> List[TemplateFolder]: """Get all template folders.""" response = self._make_request("GET", "/templateFolders") return response.json() - def create_template_folder(self, folder_data: Dict) -> Dict: + def create_template_folder(self, folder_data: Dict) -> TemplateFolder: """Create a new template folder.""" response = self._make_request("POST", "/templateFolders", json=folder_data) return response.json() - def update_template_folder(self, folder_id: str, folder_data: Dict) -> Dict: + def update_template_folder(self, folder_id: str, folder_data: Dict) -> TemplateFolder: """Update a template folder.""" response = self._make_request( "PUT", f"/templateFolders/{folder_id}", json=folder_data ) return response.json() - def delete_template_folder(self, folder_id: str) -> Dict: + def delete_template_folder(self, folder_id: str) -> DeletedResult: """Delete a template folder.""" response = self._make_request("DELETE", f"/templateFolders/{folder_id}") return response.json() @@ -31,24 +32,24 @@ def delete_template_folder(self, folder_id: str) -> Dict: class ShortcutsClient(BaseClient): """Client for shortcuts management operations.""" - def get_shortcuts(self) -> List[Dict]: + def get_shortcuts(self) -> List[Shortcut]: """Get all shortcuts.""" response = self._make_request("GET", "/shortcuts") return response.json() - def create_shortcut(self, shortcut_data: Dict) -> Dict: + def create_shortcut(self, shortcut_data: Dict) -> Shortcut: """Create a new shortcut.""" response = self._make_request("POST", "/shortcuts", json=shortcut_data) return response.json() - def update_shortcut(self, shortcut_id: str, shortcut_data: Dict) -> Dict: + def update_shortcut(self, shortcut_id: str, shortcut_data: Dict) -> Shortcut: """Update a shortcut.""" response = self._make_request( "PUT", f"/shortcuts/{shortcut_id}", json=shortcut_data ) return response.json() - def delete_shortcut(self, shortcut_id: str) -> Dict: + def delete_shortcut(self, shortcut_id: str) -> DeletedResult: """Delete a shortcut.""" response = self._make_request("DELETE", f"/shortcuts/{shortcut_id}") return response.json() @@ -57,24 +58,24 @@ def delete_shortcut(self, shortcut_id: str) -> Dict: class FeedbackClient(BaseClient): """Client for feedback management operations.""" - def get_feedback(self) -> List[Dict]: + def get_feedback(self) -> List[Feedback]: """Get all feedback.""" response = self._make_request("GET", "/feedback") return response.json() - def submit_feedback(self, feedback_data: Dict) -> Dict: + def submit_feedback(self, feedback_data: Dict) -> Feedback: """Submit new feedback.""" response = self._make_request("POST", "/feedback", json=feedback_data) return response.json() - def update_feedback(self, feedback_id: str, feedback_data: Dict) -> Dict: + def update_feedback(self, feedback_id: str, feedback_data: Dict) -> Feedback: """Update feedback.""" response = self._make_request( "PUT", f"/feedback/{feedback_id}", json=feedback_data ) return response.json() - def delete_feedback(self, feedback_id: str) -> Dict: + def delete_feedback(self, feedback_id: str) -> DeletedResult: """Delete feedback.""" response = self._make_request("DELETE", f"/feedback/{feedback_id}") return response.json() diff --git a/src/datamaker/routes/generation.py b/src/datamaker/routes/generation.py index 7cf6305..b5c563c 100644 --- a/src/datamaker/routes/generation.py +++ b/src/datamaker/routes/generation.py @@ -1,10 +1,11 @@ from .base import BaseClient +from ..types import GeneratedData class GenerationClient(BaseClient): """Client for data generation operations.""" - def generate(self, template): + def generate(self, template) -> GeneratedData: """Generate data using a template.""" response = self._make_request( "POST", diff --git a/src/datamaker/routes/keymaps.py b/src/datamaker/routes/keymaps.py index e51a8b0..3fb9016 100644 --- a/src/datamaker/routes/keymaps.py +++ b/src/datamaker/routes/keymaps.py @@ -10,6 +10,7 @@ import os from typing import Dict, List, Optional +from ..types import KeyMapLookupResult, KeyMapUpsertResult from .base import BaseClient from ..error import DataMakerError @@ -44,7 +45,7 @@ def keymap_put( entries: Dict[str, str], run_id: Optional[str] = None, project_id: Optional[str] = None, - ) -> Dict: + ) -> KeyMapUpsertResult: """Record old-to-new key mappings in a named key map (batch upsert). Use after creating records in a target system to remember which source @@ -106,7 +107,7 @@ def keymap_lookup( object: str, old_keys: List[str], project_id: Optional[str] = None, - ) -> Dict: + ) -> KeyMapLookupResult: """Translate source-system keys to target-system keys (batch lookup). Use when generating or migrating data that references records migrated diff --git a/src/datamaker/routes/masking_policies.py b/src/datamaker/routes/masking_policies.py index 636db0b..4d5a14d 100644 --- a/src/datamaker/routes/masking_policies.py +++ b/src/datamaker/routes/masking_policies.py @@ -15,6 +15,7 @@ import os from typing import Any, Dict, List, Optional +from ..types import DeletedResult, MaskingPolicy from .base import BaseClient @@ -43,7 +44,7 @@ def get_masking_policies( response = self._make_request("GET", endpoint) return response.json() - def get_masking_policy(self, policy_id: str) -> Dict[str, Any]: + def get_masking_policy(self, policy_id: str) -> MaskingPolicy: """Get a single masking policy by ID. Args: @@ -64,7 +65,7 @@ def create_masking_policy( reversible: Optional[bool] = None, key_map_name: Optional[str] = None, project_id: Optional[str] = None, - ) -> Dict[str, Any]: + ) -> MaskingPolicy: """Create a masking policy. Args: @@ -101,7 +102,7 @@ def create_masking_policy( response = self._make_request("POST", "/masking-policies", json=payload) return response.json() - def update_masking_policy(self, policy_id: str, **fields: Any) -> Dict[str, Any]: + def update_masking_policy(self, policy_id: str, **fields: Any) -> MaskingPolicy: """Update a masking policy. Args: @@ -117,7 +118,7 @@ def update_masking_policy(self, policy_id: str, **fields: Any) -> Dict[str, Any] ) return response.json() - def delete_masking_policy(self, policy_id: str) -> Dict[str, Any]: + def delete_masking_policy(self, policy_id: str) -> DeletedResult: """Delete a masking policy. Args: diff --git a/src/datamaker/routes/plans.py b/src/datamaker/routes/plans.py index 6029b15..90424be 100644 --- a/src/datamaker/routes/plans.py +++ b/src/datamaker/routes/plans.py @@ -13,6 +13,7 @@ """ from typing import Any, Dict, List +from ..types import Plan, PlanDeleteResult from .base import BaseClient @@ -20,7 +21,7 @@ class PlansClient(BaseClient): """Client for plan operations.""" - def get_plans(self) -> List[Dict[str, Any]]: + def get_plans(self) -> List[Plan]: """List the plans in the caller's active project. Returns: @@ -29,7 +30,7 @@ def get_plans(self) -> List[Dict[str, Any]]: response = self._make_request("GET", "/plans") return response.json() - def get_plan(self, plan_id: str) -> Dict[str, Any]: + def get_plan(self, plan_id: str) -> Plan: """Get a single plan by ID, including its full ``spec`` and ``history``. Args: @@ -41,7 +42,7 @@ def get_plan(self, plan_id: str) -> Dict[str, Any]: response = self._make_request("GET", f"/plans/{plan_id}") return response.json() - def update_plan(self, plan_id: str, **fields: Any) -> Dict[str, Any]: + def update_plan(self, plan_id: str, **fields: Any) -> Plan: """Update a plan. Args: @@ -54,7 +55,7 @@ def update_plan(self, plan_id: str, **fields: Any) -> Dict[str, Any]: response = self._make_request("PATCH", f"/plans/{plan_id}", json=fields) return response.json() - def delete_plan(self, plan_id: str) -> Dict[str, Any]: + def delete_plan(self, plan_id: str) -> PlanDeleteResult: """Delete a plan. Note: diff --git a/src/datamaker/routes/projects.py b/src/datamaker/routes/projects.py index f670e22..cc865ed 100644 --- a/src/datamaker/routes/projects.py +++ b/src/datamaker/routes/projects.py @@ -1,35 +1,36 @@ from .base import BaseClient from typing import Optional, Dict, List +from ..types import DeletedResult, Project class ProjectsClient(BaseClient): """Client for project management operations.""" - def get_projects(self) -> List[Dict]: + def get_projects(self) -> List[Project]: """Get all projects.""" response = self._make_request("GET", "/projects") return response.json() - def create_project(self, project_data: Dict, team_id: str) -> Dict: + def create_project(self, project_data: Dict, team_id: str) -> Project: """Create a new project.""" # Ensure required teamId is present project_data["teamId"] = team_id response = self._make_request("POST", "/projects", json=project_data) return response.json() - def get_project(self, project_id: str) -> Dict: + def get_project(self, project_id: str) -> Project: """Get a specific project by ID.""" response = self._make_request("GET", f"/projects/{project_id}") return response.json() - def update_project(self, project_id: str, project_data: Dict) -> Dict: + def update_project(self, project_id: str, project_data: Dict) -> Project: """Update a project.""" response = self._make_request( "PUT", f"/projects/{project_id}", json=project_data ) return response.json() - def delete_project(self, project_id: str) -> Dict: + def delete_project(self, project_id: str) -> DeletedResult: """Delete a project.""" response = self._make_request("DELETE", f"/projects/{project_id}") return response.json() diff --git a/src/datamaker/routes/scenario_files.py b/src/datamaker/routes/scenario_files.py index b70ea5b..09f63f1 100644 --- a/src/datamaker/routes/scenario_files.py +++ b/src/datamaker/routes/scenario_files.py @@ -5,6 +5,7 @@ import mimetypes import requests from typing import Dict, List, Optional, Union, BinaryIO +from ..types import ScenarioFile, ScenarioFileCreated, SuccessResult, WorkspaceUploadResult from .base import BaseClient from ..error import DataMakerError @@ -49,7 +50,7 @@ def get_scenario_files( def get_scenario_file( self, file_id: str, scenario_id: Optional[str] = None - ) -> Dict: + ) -> ScenarioFile: """Get metadata for a specific file by ID. Args: @@ -141,7 +142,7 @@ def create_scenario_file( description: Optional[str] = None, mime_type: Optional[str] = None, folder_id: Optional[str] = None, - ) -> Dict: + ) -> ScenarioFileCreated: """Create/upload a new file in a scenario. Args: @@ -222,7 +223,7 @@ def upload_scenario_file_from_path( description: Optional[str] = None, folder_id: Optional[str] = None, folder: str = "uploads", - ) -> Dict: + ) -> WorkspaceUploadResult: """Upload a file from a local path to a scenario using multipart upload. Args: @@ -272,7 +273,7 @@ def upload_scenario_file_from_path( def delete_scenario_file( self, file_id: str, scenario_id: Optional[str] = None - ) -> Dict: + ) -> SuccessResult: """Delete a file by ID. Args: @@ -400,7 +401,7 @@ def save_file( description: Optional[str] = None, folder_id: Optional[str] = None, folder: str = "uploads", - ) -> Dict: + ) -> WorkspaceUploadResult: """Convenience method to save a local file to workspace storage. This method simplifies uploading files by automatically pulling required diff --git a/src/datamaker/routes/sets.py b/src/datamaker/routes/sets.py index 875805f..31ce85f 100644 --- a/src/datamaker/routes/sets.py +++ b/src/datamaker/routes/sets.py @@ -8,6 +8,7 @@ import os from typing import Dict, List, Optional, Any +from ..types import DeletedResult, Set from .base import BaseClient from ..error import DataMakerError @@ -54,7 +55,7 @@ def create_set( description: Optional[str] = None, row_count: Optional[int] = None, project_id: Optional[str] = None, - ) -> Dict: + ) -> Set: """Create (save) a new set. Args: @@ -99,7 +100,7 @@ def update_set( description: Optional[str] = None, data: Optional[Any] = None, row_count: Optional[int] = None, - ) -> Dict: + ) -> Set: """Update a saved set. Only the fields that are provided are sent; the rest are left unchanged. @@ -128,7 +129,7 @@ def update_set( response = self._make_request("PATCH", f"/sets/{set_id}", json=update_data) return response.json() - def delete_set(self, set_id: str) -> Dict: + def delete_set(self, set_id: str) -> DeletedResult: """Delete a saved set by ID. Args: @@ -146,7 +147,7 @@ def save_set( data: Any, description: Optional[str] = None, project_id: Optional[str] = None, - ) -> Dict: + ) -> Set: """Convenience method to save rows as a named set. This is the most common entry point: hand it a name and the rows you diff --git a/src/datamaker/routes/teams.py b/src/datamaker/routes/teams.py index 12946b1..7ee7873 100644 --- a/src/datamaker/routes/teams.py +++ b/src/datamaker/routes/teams.py @@ -1,31 +1,32 @@ from .base import BaseClient from typing import Optional, Dict, List +from ..types import DeletedResult, MemberInviteResult, Team, TeamMember, TeamSetupResult class TeamsClient(BaseClient): """Client for team management operations.""" - def get_teams(self) -> List[Dict]: + def get_teams(self) -> List[Team]: """Get all teams.""" response = self._make_request("GET", "/teams") return response.json() - def create_team(self, team_data: Dict) -> Dict: + def create_team(self, team_data: Dict) -> Team: """Create a new team.""" response = self._make_request("POST", "/teams", json=team_data) return response.json() - def update_team(self, team_id: str, team_data: Dict) -> Dict: + def update_team(self, team_id: str, team_data: Dict) -> Team: """Update a team.""" response = self._make_request("PUT", f"/teams/{team_id}", json=team_data) return response.json() - def delete_team(self, team_id: str) -> Dict: + def delete_team(self, team_id: str) -> DeletedResult: """Delete a team.""" response = self._make_request("DELETE", f"/teams/{team_id}") return response.json() - def setup_team(self, team_data: Dict) -> Dict: + def setup_team(self, team_data: Dict) -> TeamSetupResult: """Setup a new team.""" response = self._make_request("POST", "/setup/teams", json=team_data) return response.json() @@ -34,29 +35,29 @@ def setup_team(self, team_data: Dict) -> Dict: class TeamMembersClient(BaseClient): """Client for team member management operations.""" - def get_team_members(self) -> List[Dict]: + def get_team_members(self) -> List[TeamMember]: """Get all team members.""" response = self._make_request("GET", "/teamMembers") return response.json() - def add_team_member(self, member_data: Dict) -> Dict: + def add_team_member(self, member_data: Dict) -> TeamMember: """Add a new team member.""" response = self._make_request("POST", "/teamMembers", json=member_data) return response.json() - def invite_team_member(self, invite_data: Dict) -> Dict: + def invite_team_member(self, invite_data: Dict) -> MemberInviteResult: """Invite a new team member.""" response = self._make_request("POST", "/teamMembers/invite", json=invite_data) return response.json() - def update_team_member(self, member_id: str, member_data: Dict) -> Dict: + def update_team_member(self, member_id: str, member_data: Dict) -> TeamMember: """Update a team member.""" response = self._make_request( "PUT", f"/teamMembers/{member_id}", json=member_data ) return response.json() - def remove_team_member(self, member_id: str) -> Dict: + def remove_team_member(self, member_id: str) -> DeletedResult: """Remove a team member.""" response = self._make_request("DELETE", f"/teamMembers/{member_id}") return response.json() diff --git a/src/datamaker/routes/templates.py b/src/datamaker/routes/templates.py index 6331c64..1408d44 100644 --- a/src/datamaker/routes/templates.py +++ b/src/datamaker/routes/templates.py @@ -1,19 +1,20 @@ from .base import BaseClient from ..error import DataMakerError from typing import Dict, List +from ..types import DeletedResult, Template class TemplatesClient(BaseClient): """Client for template operations.""" - def get_templates(self) -> List[Dict]: + def get_templates(self) -> List[Template]: """Fetch all templates from the API.""" response = self._make_request("GET", "/templates") return response.json() def create_template( self, template_data: Dict, project_id: str, team_id: str - ) -> Dict: + ) -> Template: """Create a new template.""" # Ensure required fields are present template_data["projectId"] = project_id @@ -28,24 +29,24 @@ def create_template( response = self._make_request("POST", "/templates", json=template_data) return response.json() - def get_template(self, template_id: str) -> Dict: + def get_template(self, template_id: str) -> Template: """Get a specific template by ID.""" response = self._make_request("GET", f"/templates/{template_id}") return response.json() - def update_template(self, template_id: str, template_data: Dict) -> Dict: + def update_template(self, template_id: str, template_data: Dict) -> Template: """Update a template.""" response = self._make_request( "PUT", f"/templates/{template_id}", json=template_data ) return response.json() - def delete_template(self, template_id: str) -> Dict: + def delete_template(self, template_id: str) -> DeletedResult: """Delete a template.""" response = self._make_request("DELETE", f"/templates/{template_id}") return response.json() - def get_template_by_id(self, template_id: str) -> Dict: + def get_template_by_id(self, template_id: str) -> Template: """Get a specific template by ID (legacy method for backward compatibility).""" try: # Try direct API call first (more efficient) diff --git a/src/datamaker/routes/users.py b/src/datamaker/routes/users.py index 5624c52..07aae40 100644 --- a/src/datamaker/routes/users.py +++ b/src/datamaker/routes/users.py @@ -1,43 +1,49 @@ from .base import BaseClient from typing import Optional, Dict, List +from ..types import CurrentUser, DeletedResult, User class UsersClient(BaseClient): """Client for user management operations.""" - def get_users(self) -> List[Dict]: + def get_users(self) -> List[User]: """Get all users.""" response = self._make_request("GET", "/users") return response.json() - def create_user(self, user_data: Dict, user_id: str) -> Dict: + def create_user(self, user_data: Dict, user_id: str) -> User: """Create a new user.""" # Ensure required id is present user_data["id"] = user_id response = self._make_request("POST", "/users", json=user_data) return response.json() - def get_current_user(self) -> Dict: + def get_current_user(self) -> CurrentUser: """Get current user information.""" response = self._make_request("GET", "/users/me") return response.json() - def provision_user(self, user_data: Dict) -> Dict: - """Provision a new user.""" + def provision_user(self, user_data: Dict) -> DeletedResult: + """Provision a new user. + + Returns `DeletedResult` despite the name: the API answers this route + with a bare `{message}` and puts the meaning in the status code, and + `DeletedResult` is the schema it shares with the delete routes. + """ response = self._make_request("POST", "/users/provision", json=user_data) return response.json() - def update_user(self, user_id: str, user_data: Dict) -> Dict: + def update_user(self, user_id: str, user_data: Dict) -> User: """Update a user.""" response = self._make_request("PUT", f"/users/{user_id}", json=user_data) return response.json() - def patch_user(self, user_id: str, user_data: Dict) -> Dict: + def patch_user(self, user_id: str, user_data: Dict) -> User: """Partially update a user.""" response = self._make_request("PATCH", f"/users/{user_id}", json=user_data) return response.json() - def delete_user(self, user_id: str) -> Dict: + def delete_user(self, user_id: str) -> DeletedResult: """Delete a user.""" response = self._make_request("DELETE", f"/users/{user_id}") return response.json() diff --git a/src/datamaker/types.py b/src/datamaker/types.py new file mode 100644 index 0000000..3e118f5 --- /dev/null +++ b/src/datamaker/types.py @@ -0,0 +1,136 @@ +"""Spec schema names, resolved to the classes the generator produced. + +Generated by scripts/generate_schema.py - do not edit by hand. + +Import types from HERE, not from `generated.schema`: the generator lets an +inline schema take a bare name like `Plan` and renames the real component to +`Plan1`, so the numbering is not stable across spec changes. This module is +re-derived by matching key sets against the spec on every generation. +""" + +from .generated import schema as _s + +# PackInstallListItem: allOf composition (PackInstall + installedByName) +# SchemaGraphNavigation: only referenced inside a union; no standalone class +# SetDetail: allOf composition (Set + createdByName) is merged away + +AddressAvailability = _s.AddressAvailability +ApiError = _s.ApiError +ApiKey = _s.ApiKey +ApiKeyValidation = _s.ApiKeyValidation +ApiMessageError = _s.ApiMessageError +ApprovalDecision = _s.ApprovalDecision +ApprovalStatus = _s.ApprovalStatus +AuditEvent = _s.AuditEvent +AuditEventRef = _s.AuditEventRef +BuiltinSkill = _s.BuiltinSkill +CapabilityCatalogItem = _s.CapabilityCatalogItem +Chat = _s.Chat +ChatAsset = _s.ChatAsset +ClassifiedFields = _s.ClassifiedFields +ConnectionTable = _s.ConnectionTable +ConnectionVerdict = _s.ConnectionVerdict +CsrfToken = _s.CsrfToken +CsvBatchUploadError = _s.CsvBatchUploadError +CsvBatchUploadResult = _s.CsvBatchUploadResult +CurrentUser = _s.CurrentUser +CustomDataType = _s.CustomDataType +DatabaseExportResult = _s.DatabaseExportResult +DatabaseTemplatesProposal = _s.DatabaseTemplatesProposal +DatamakerConfig = _s.DatamakerConfig +DeletedResult = _s.DeletedResult +EffectivePermissions = _s.EffectivePermissions +Endpoint = _s.Endpoint +EndpointFolder = _s.EndpointFolder +Feedback = _s.Feedback +FieldType = _s.FieldType +GeneratedData = _s.GeneratedData +GeneratedTemplateField = _s.GeneratedTemplateField +GeneratedTemplateFields = _s.GeneratedTemplateFields +ImageAnalysisResult = _s.ImageAnalysisResult +Integration = _s.Integration +KeyMapDeleteResult = _s.KeyMapDeleteResult +KeyMapEntriesPage = _s.KeyMapEntriesPage +KeyMapEntry = _s.KeyMapEntry +KeyMapLookupResult = _s.KeyMapLookupResult +KeyMapSummary = _s.KeyMapSummary +KeyMapUpsertResult = _s.KeyMapUpsertResult +License = _s.License +LicenseStatus = _s.LicenseStatus +LogCleanupResult = _s.LogCleanupResult +LogoutResult = _s.LogoutResult +MaskingPolicy = _s.MaskingPolicy +MemberInviteResult = _s.MemberInviteResult +MemberRoleAssignment = _s.MemberRoleAssignment +PackCatalog = _s.PackCatalog +PackCreatedCounts = _s.PackCreatedCounts +PackExportResult = _s.PackExportResult +PackImportDiff = _s.PackImportDiff +PackInstall = _s.PackInstall +PackInstallResult = _s.PackInstallResult +PdfAnalysisResult = _s.PdfAnalysisResult +PermissionCatalogue = _s.PermissionCatalogue +Plan = _s.Plan1 +PlanDeleteResult = _s.PlanDeleteResult +PlanRun = _s.PlanRun +PlanRunAccepted = _s.PlanRunAccepted +PlanRunAck = _s.PlanRunAck +PlanRunFile = _s.PlanRunFile +PlanRunFileAck = _s.PlanRunFileAck +PlanSignoff = _s.PlanSignoff +PreferencesUpdateError = _s.PreferencesUpdateError +PreferencesUpdateResult = _s.PreferencesUpdateResult +PreviewResult = _s.PreviewResult +Project = _s.Project +PythonExecutionError = _s.PythonExecutionError +PythonExecutionResult = _s.PythonExecutionResult +Role = _s.Role +SapServiceCatalog = _s.SapServiceCatalog +Scenario = _s.Scenario1 +ScenarioDiagramRegenerateAccepted = _s.ScenarioDiagramRegenerateAccepted +ScenarioEnvironmentVariableList = _s.ScenarioEnvironmentVariableList +ScenarioEnvironmentVariables = _s.ScenarioEnvironmentVariables +ScenarioFile = _s.ScenarioFile +ScenarioFileCreated = _s.ScenarioFileCreated +ScenarioJob = _s.ScenarioJob +ScenarioJobStatus = _s.ScenarioJobStatus +ScenarioJobs = _s.ScenarioJobs +ScenarioLog = _s.ScenarioLog +SchemaGraphChange = _s.SchemaGraphChange +SchemaGraphConnectResult = _s.SchemaGraphConnectResult +SchemaGraphDiffResult = _s.SchemaGraphDiffResult +SchemaGraphEntity = _s.SchemaGraphEntity +SchemaGraphEntityContext = _s.SchemaGraphEntityContext +SchemaGraphMinedField = _s.SchemaGraphMinedField +SchemaGraphPathResult = _s.SchemaGraphPathResult +SchemaGraphPreviewResult = _s.SchemaGraphPreviewResult +SchemaGraphProperty = _s.SchemaGraphProperty +SchemaGraphRefreshResult = _s.SchemaGraphRefreshResult +SchemaGraphSampleResult = _s.SchemaGraphSampleResult +SchemaGraphSearchResult = _s.SchemaGraphSearchResult +SchemaGraphTemplateResult = _s.SchemaGraphTemplateResult +SessionTokens = _s.SessionTokens +Set = _s.Set +Shortcut = _s.Shortcut +Skill = _s.Skill +StringList = _s.StringList +SuccessResult = _s.SuccessResult +Team = _s.Team +TeamMember = _s.TeamMember +TeamSetupResult = _s.TeamSetupResult +TeamSkill = _s.TeamSkill +Template = _s.Template2 +TemplateFolder = _s.TemplateFolder +ToscaOnPremWorkspaces = _s.ToscaOnPremWorkspaces +ToscaWorkspaces = _s.ToscaWorkspaces +UploadResult = _s.UploadResult +UploadTextResult = _s.UploadTextResult +User = _s.User +UserPreferences = _s.UserPreferences +WorkerWorkspaceInfo = _s.WorkerWorkspaceInfo +WorkerWorkspaceListing = _s.WorkerWorkspaceListing +WorkerWorkspaceSyncResult = _s.WorkerWorkspaceSyncResult +WorkerWorkspaceUploadResult = _s.WorkerWorkspaceUploadResult +WorkspaceListing = _s.WorkspaceListing +WorkspaceStorage = _s.WorkspaceStorage +WorkspaceUploadResult = _s.WorkspaceUploadResult