{
  "openapi": "3.0.3",
  "info": {
    "title": "Configuration Hub API",
    "description": "Lightweight index API for exploring epilot organization configurations.\n\nProvides a unified tree view across all config types. Returns summary metadata only —\nthe frontend calls individual epilot APIs directly (via @epilot/sdk) for full config payloads.\n\n## Source APIs per resource type\n\nEach resource type maps to a specific epilot API. The frontend should use the corresponding\n@epilot/sdk client to fetch full config details (e.g., for the side panel JSON view).\n\n| Resource Type | Source API | SDK Client |\n|---|---|---|\n| `journey` | journey.sls.epilot.io | `@epilot/sdk/journey` |\n| `automation_flow` | automation.sls.epilot.io | `@epilot/sdk/automation` |\n| `workflow_definition` | workflow-definition.sls.epilot.io | `@epilot/sdk/workflow-definition` |\n| `closing_reason` | workflow-definition.sls.epilot.io | `@epilot/sdk/workflow-definition` |\n| `flow_template` | workflow-definition.sls.epilot.io | `@epilot/sdk/workflow-definition` |\n| `schema` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `taxonomy` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `taxonomy_classification` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `emailtemplate` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `product` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `price` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `tax` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `coupon` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `file` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `document_template` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `webhook` | webhooks.sls.epilot.io | `@epilot/sdk/webhooks` |\n| `saved_view` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `dashboard` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `kanban` | kanban.sls.epilot.io | `@epilot/sdk/kanban` |\n| `role` | permissions.sls.epilot.io | `@epilot/sdk/permissions` |\n| `usergroup` | user.sls.epilot.io | `@epilot/sdk/user` |\n| `validation_rule` | entity.sls.epilot.io | `@epilot/sdk/validation-rules` |\n| `integration` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `app` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `designbuilder` | design-builder-api.sls.epilot.io | `@epilot/sdk/design` |\n| `notification_template` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `custom_variable` | entity.sls.epilot.io | `@epilot/sdk/template-variables` |\n| `environment_variable` | environments.sls.epilot.io | `@epilot/sdk/environments` |\n| `entity_mapping` | entity-mapping.sls.epilot.io | `@epilot/sdk/entity-mapping` |\n| `portal_config` | customer-portal.sls.epilot.io | `@epilot/sdk/customer-portal` |\n| `target` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `product_recommendation` | entity.sls.epilot.io | `@epilot/sdk/entity` |\n| `access_token` | access-token.sls.epilot.io | `@epilot/sdk/access-token` |\n",
    "version": "0.2.0"
  },
  "servers": [
    {
      "url": "https://configuration-hub.sls.epilot.io"
    },
    {
      "url": "https://configuration-hub.dev.sls.epilot.io",
      "description": "Dev"
    }
  ],
  "security": [
    {
      "EpilotAuth": []
    }
  ],
  "tags": [
    {
      "name": "Configs",
      "description": "Configuration tree index"
    },
    {
      "name": "Sync",
      "description": "Cross-org configuration sync jobs"
    }
  ],
  "paths": {
    "/v1/configs/types": {
      "get": {
        "operationId": "listConfigTypes",
        "summary": "listConfigTypes",
        "description": "Returns the static list of available configuration types with display metadata.\nThis is a cheap call — no fan-out to downstream APIs. Returns all known types\nwith labels and icons. The frontend should then call `listConfigs` separately\nfor each type it wants to load.\n",
        "tags": [
          "Configs"
        ],
        "responses": {
          "200": {
            "description": "Available config types",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ConfigTypeInfo"
                      }
                    }
                  },
                  "required": [
                    "results"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/{type}": {
      "get": {
        "operationId": "listConfigs",
        "summary": "listConfigs",
        "description": "List configs of a given type with pagination. Returns summary metadata only\n(not full payloads). The frontend calls this per type folder when expanding.\n\nSupports offset-based pagination via `from` and `size` parameters.\n",
        "tags": [
          "Configs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConfigType"
          },
          {
            "$ref": "#/components/parameters/CursorParam"
          },
          {
            "$ref": "#/components/parameters/SizeParam"
          },
          {
            "name": "q",
            "in": "query",
            "description": "Search query to filter configs by name/title",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "updated_after",
            "in": "query",
            "description": "Filter configs updated after this date (ISO 8601)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "updated_before",
            "in": "query",
            "description": "Filter configs updated before this date (ISO 8601)",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "purposes",
            "in": "query",
            "description": "Filter by purpose classification IDs (comma-separated)",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "blueprint_ids",
            "in": "query",
            "description": "Filter by installed-blueprint IDs (comma-separated). Only configs installed by one of the listed blueprints are returned.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort order. `updated_at` (default) sorts by most recently modified.\n`usage` sorts by the type-specific usage metric descending\n(submissions for journeys, executions for automations, entities for schemas, etc.).\n",
            "schema": {
              "type": "string",
              "enum": [
                "updated_at",
                "usage"
              ],
              "default": "updated_at"
            }
          },
          {
            "name": "active_only",
            "in": "query",
            "description": "If true, filter out configs that are explicitly inactive (active=false).\nConfigs with no `active` field are always included.\n",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated config list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigListResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid config type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/{type}/{id}/dependencies": {
      "get": {
        "operationId": "getConfigDependencies",
        "summary": "getConfigDependencies",
        "description": "Get configs that are referenced by the given config.\nUsed to render children when expanding a config node in the tree.\n\nResolves dependencies by fetching the config payload server-side and scanning\nfor references (UUIDs, source IDs, slug-based references).\n",
        "tags": [
          "Configs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConfigType"
          },
          {
            "$ref": "#/components/parameters/ConfigId"
          },
          {
            "$ref": "#/components/parameters/CursorParam"
          },
          {
            "$ref": "#/components/parameters/SizeParam"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of referenced configs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigDependenciesResponse"
                }
              }
            }
          },
          "404": {
            "description": "Config not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/{type}/{id}/used_by": {
      "get": {
        "operationId": "getConfigUsedBy",
        "summary": "getConfigUsedBy",
        "description": "Get configs that reference the given config (reverse dependencies).\nScans the indexed config items for references to this config's ID or aliases.\n",
        "tags": [
          "Configs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/ConfigType"
          },
          {
            "$ref": "#/components/parameters/ConfigId"
          }
        ],
        "responses": {
          "200": {
            "description": "List of configs that reference this config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigDependenciesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/index": {
      "get": {
        "operationId": "getIndex",
        "summary": "getIndex",
        "description": "Return the current index build state for the caller's organization.\nClients poll this to decide whether to show a \"building\" indicator\nand when to refetch data.\n",
        "tags": [
          "Configs"
        ],
        "responses": {
          "200": {
            "description": "Current index status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexStatusResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/sync-jobs": {
      "post": {
        "operationId": "createSyncJob",
        "summary": "createSyncJob",
        "description": "Create a new cross-org sync job. The job is enqueued for asynchronous execution\nby the worker Lambda; the response returns the persisted job header with status\n`pending`.\n\nSee `docs/sync/INTERFACES.md` for the locked request/response contract.\n",
        "tags": [
          "Sync"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncJobRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sync job accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncJob"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listSyncJobs",
        "summary": "listSyncJobs",
        "description": "List sync jobs scoped to the caller's organization, paginated with an opaque\ncursor. Defaults to most-recent first.\n",
        "tags": [
          "Sync"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/CursorParam"
          },
          {
            "$ref": "#/components/parameters/SizeParam"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter jobs by status",
            "schema": {
              "$ref": "#/components/schemas/SyncJobStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sync jobs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncJobListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/sync-jobs/{id}": {
      "get": {
        "operationId": "getSyncJob",
        "summary": "getSyncJob",
        "description": "Fetch a single sync job by ID. Returns the job header, counts summary,\ncurrent phase pointer, and the latest activity events. Frontend polls this\nendpoint with a ramping interval.\n",
        "tags": [
          "Sync"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SyncJobId"
          }
        ],
        "responses": {
          "200": {
            "description": "Sync job detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncJob"
                }
              }
            }
          },
          "404": {
            "description": "Sync job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/sync-jobs/{id}/retry": {
      "post": {
        "operationId": "retrySyncJob",
        "summary": "retrySyncJob",
        "description": "Retry the failed resources from a prior sync job. Creates a new job whose\nscope is the failed `(type, source_id)` set of the original job and enqueues\nit for execution. Optionally accepts inline payload overrides.\n",
        "tags": [
          "Sync"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SyncJobId"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncJobRetryRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Retry job accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncJob"
                }
              }
            }
          },
          "404": {
            "description": "Sync job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/sync-jobs/{id}/resources": {
      "get": {
        "operationId": "listSyncJobResources",
        "summary": "listSyncJobResources",
        "description": "List the per-resource rows for a sync job. Supports filtering by status\n(e.g. `failed`) and cursor pagination. Used by the failures table and the\ndry-run plan view in the frontend.\n",
        "tags": [
          "Sync"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/SyncJobId"
          },
          {
            "$ref": "#/components/parameters/CursorParam"
          },
          {
            "$ref": "#/components/parameters/SizeParam"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter resources by status",
            "schema": {
              "$ref": "#/components/schemas/SyncJobResourceStatus"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated sync job resources",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SyncJobResourceListResponse"
                }
              }
            }
          },
          "404": {
            "description": "Sync job not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/configs/index:rebuild": {
      "post": {
        "operationId": "rebuildIndex",
        "summary": "rebuildIndex",
        "description": "Rebuild the configuration index for the caller's organization.\nFire-and-forget: invokes the async worker and returns immediately.\nA new rebuild will cancel any in-flight build (see `build_token`).\n",
        "tags": [
          "Configs"
        ],
        "responses": {
          "200": {
            "description": "Index build status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexRebuildResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "EpilotAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "epilot JWT auth token"
      }
    },
    "parameters": {
      "ConfigType": {
        "name": "type",
        "in": "path",
        "required": true,
        "description": "Configuration resource type",
        "schema": {
          "$ref": "#/components/schemas/ResourceType"
        }
      },
      "ConfigId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Configuration resource ID",
        "schema": {
          "type": "string"
        }
      },
      "CursorParam": {
        "name": "cursor",
        "in": "query",
        "description": "Opaque cursor for fetching the next page. Omit for the first page.",
        "schema": {
          "type": "string"
        }
      },
      "SizeParam": {
        "name": "size",
        "in": "query",
        "description": "Number of items per page",
        "schema": {
          "type": "integer",
          "default": 25,
          "minimum": 1,
          "maximum": 100
        }
      },
      "SyncJobId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Sync job ID",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "ResourceType": {
        "type": "string",
        "description": "Configuration resource type identifier.\nMatches blueprint-manifest-api V3 naming conventions.\n",
        "enum": [
          "journey",
          "automation_flow",
          "workflow_definition",
          "closing_reason",
          "flow_template",
          "schema",
          "emailtemplate",
          "product",
          "price",
          "tax",
          "coupon",
          "file",
          "document_template",
          "webhook",
          "saved_view",
          "dashboard",
          "kanban",
          "role",
          "usergroup",
          "validation_rule",
          "integration",
          "app",
          "designbuilder",
          "notification_template",
          "custom_variable",
          "environment_variable",
          "taxonomy",
          "taxonomy_classification",
          "entity_mapping",
          "portal_config",
          "target",
          "product_recommendation",
          "access_token"
        ]
      },
      "ConfigTypeInfo": {
        "type": "object",
        "description": "Static metadata for a config type folder in the tree.\nNo downstream API calls — just type + label + icon + source API info.\n",
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ResourceType"
          },
          "label": {
            "type": "string",
            "description": "Display label",
            "example": "Journeys"
          },
          "icon": {
            "type": "string",
            "description": "Frontend icon name",
            "example": "Route"
          },
          "source_api": {
            "type": "string",
            "description": "Base URL of the epilot API that owns this resource type",
            "example": "https://journey.sls.epilot.io"
          },
          "sdk_client": {
            "type": "string",
            "description": "@epilot/sdk subpath for fetching full config payloads",
            "example": "@epilot/sdk/journey"
          }
        },
        "required": [
          "type",
          "label",
          "icon",
          "source_api",
          "sdk_client"
        ]
      },
      "ConfigNode": {
        "type": "object",
        "description": "Summary metadata for a single configuration item in the tree",
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ResourceType"
          },
          "id": {
            "type": "string",
            "description": "Unique identifier"
          },
          "title": {
            "type": "string",
            "description": "Display name"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last modified timestamp"
          },
          "updated_by": {
            "type": "string",
            "description": "User who last modified this config"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Tags / labels"
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Alternative identifiers (short IDs, slugs, variable keys) used in cross-references"
          },
          "purposes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Business purposes"
          },
          "link": {
            "type": "string",
            "format": "uri",
            "description": "Direct link to open this config in epilot"
          },
          "active": {
            "type": "boolean",
            "description": "Whether this config is currently active/enabled (omitted when not applicable)"
          },
          "blueprints": {
            "type": "array",
            "description": "Installed blueprints that produced this config (tagged during the rebuild lineage pass)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "title"
              ]
            }
          },
          "metadata": {
            "type": "object",
            "description": "Type-specific metadata (e.g., submission count for journeys)",
            "additionalProperties": true
          }
        },
        "required": [
          "type",
          "id",
          "title"
        ]
      },
      "ConfigListResponse": {
        "type": "object",
        "description": "Cursor-paginated list of configs for a specific type",
        "properties": {
          "type": {
            "$ref": "#/components/schemas/ResourceType"
          },
          "label": {
            "type": "string"
          },
          "icon": {
            "type": "string"
          },
          "total": {
            "type": "integer",
            "description": "Total number of configs of this type (if known)"
          },
          "next_cursor": {
            "type": "string",
            "description": "Cursor for fetching the next page. Absent when no more pages."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigNode"
            }
          }
        },
        "required": [
          "type",
          "label",
          "icon",
          "results"
        ]
      },
      "ConfigDependenciesResponse": {
        "type": "object",
        "description": "Cursor-paginated list of configs referenced by a given config",
        "properties": {
          "total": {
            "type": "integer",
            "description": "Total number of dependencies found (if known)"
          },
          "next_cursor": {
            "type": "string",
            "description": "Cursor for fetching the next page. Absent when no more pages."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ConfigNode"
            }
          }
        },
        "required": [
          "results"
        ]
      },
      "IndexRebuildResponse": {
        "type": "object",
        "description": "Result of an index rebuild operation",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ready",
              "building",
              "failed",
              "already_building"
            ]
          },
          "last_built_at": {
            "type": "string",
            "format": "date-time"
          },
          "total_items": {
            "type": "integer"
          },
          "build_duration_ms": {
            "type": "integer"
          },
          "failed_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "status"
        ]
      },
      "IndexStatusResponse": {
        "type": "object",
        "description": "Current index build state",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "missing",
              "building",
              "ready",
              "failed"
            ]
          },
          "last_built_at": {
            "type": "string",
            "format": "date-time"
          },
          "total_items": {
            "type": "integer"
          },
          "build_duration_ms": {
            "type": "integer"
          }
        },
        "required": [
          "status"
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "error"
        ]
      },
      "SyncJobStatus": {
        "type": "string",
        "description": "Lifecycle status of a sync job. See `docs/sync/INTERFACES.md` for state\ntransitions.\n",
        "enum": [
          "pending",
          "in_progress",
          "succeeded",
          "partial",
          "failed",
          "cancelled"
        ]
      },
      "SyncDirection": {
        "type": "string",
        "description": "Direction of the sync, derived from the source/target pane selection in the\nconfiguration hub UI.\n",
        "enum": [
          "push",
          "pull"
        ]
      },
      "SyncPhase": {
        "type": "string",
        "description": "Three-phase orchestrator phase. `phase_0` fetches source payloads,\n`phase_a` creates/matches with topological batches, `phase_a5` resolves\nderived references, `phase_b` patches with the full ID map, `finalize`\nruns cycle-breaking finalizers.\n",
        "enum": [
          "phase_0",
          "phase_a",
          "phase_a5",
          "phase_b",
          "finalize"
        ]
      },
      "SyncJobResourceStatus": {
        "type": "string",
        "description": "Per-resource status. `would_*` values are produced by dry-run jobs.\n",
        "enum": [
          "pending",
          "in_progress",
          "created",
          "patched",
          "skipped",
          "would_create",
          "would_patch",
          "would_skip_unchanged",
          "would_match_heuristic",
          "failed"
        ]
      },
      "SyncJobRequest": {
        "type": "object",
        "description": "Request body for `createSyncJob`. `target_auth_token` is the destination\norg's auth token and MUST NOT be persisted or logged — it is `writeOnly`.\n",
        "required": [
          "source_org_id",
          "target_org_id",
          "target_auth_token",
          "resources"
        ],
        "properties": {
          "source_org_id": {
            "type": "string",
            "description": "Org ID the resources are sourced from."
          },
          "target_org_id": {
            "type": "string",
            "description": "Org ID the resources are written into."
          },
          "target_auth_token": {
            "type": "string",
            "writeOnly": true,
            "description": "Destination-org auth token forwarded to adapter writes. Never returned\nin responses and never logged.\n"
          },
          "name": {
            "type": "string",
            "description": "Optional human-friendly job name shown in the history list."
          },
          "dry_run": {
            "type": "boolean",
            "default": false,
            "description": "If true, the orchestrator runs Phase 0 + a planning pass and writes\n`would_*` resource rows but performs no destination writes.\n"
          },
          "include_dependencies": {
            "type": "boolean",
            "default": true,
            "description": "If true, the orchestrator expands the resource set by following\ndependency edges discovered during Phase 0.\n"
          },
          "resources": {
            "type": "array",
            "description": "Initial resource selection. Dependencies may be added.",
            "items": {
              "type": "object",
              "required": [
                "type",
                "id"
              ],
              "properties": {
                "type": {
                  "type": "string",
                  "description": "Resource type identifier"
                },
                "id": {
                  "type": "string",
                  "description": "Source-org resource ID"
                }
              }
            }
          }
        }
      },
      "SyncJobRetryRequest": {
        "type": "object",
        "description": "Optional body for `retrySyncJob`. Defaults to retrying every failed\nresource of the original job.\n",
        "properties": {
          "payload_overrides": {
            "type": "object",
            "description": "Map of `<type>:<source_id>` → partial payload patch. Applied on top of\nthe originally fetched payload before re-running Phase A.\n",
            "additionalProperties": true
          }
        }
      },
      "SyncJobCounts": {
        "type": "object",
        "description": "Aggregate counters by resource status.",
        "properties": {
          "total": {
            "type": "integer"
          },
          "pending": {
            "type": "integer"
          },
          "in_progress": {
            "type": "integer"
          },
          "succeeded": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          },
          "skipped_unchanged": {
            "type": "integer"
          }
        },
        "required": [
          "total",
          "pending",
          "in_progress",
          "succeeded",
          "failed",
          "skipped_unchanged"
        ]
      },
      "SyncJobEvent": {
        "type": "object",
        "description": "Activity-log entry surfaced to the frontend. Backed by the op-log rows in\nthe index table (`SYNC#<jobId>#OP#<seq>`).\n",
        "properties": {
          "seq": {
            "type": "integer",
            "description": "Monotonic sequence number assigned at write time."
          },
          "ts": {
            "type": "string",
            "format": "date-time"
          },
          "phase": {
            "$ref": "#/components/schemas/SyncPhase"
          },
          "type": {
            "type": "string",
            "description": "Resource type"
          },
          "source_id": {
            "type": "string"
          },
          "target_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SyncJobResourceStatus"
          },
          "message": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "seq",
          "ts",
          "status"
        ]
      },
      "SyncJobBatch": {
        "type": "object",
        "description": "Position within the current topological batch for the active phase.",
        "properties": {
          "index": {
            "type": "integer",
            "description": "Zero-based index of the batch currently executing."
          },
          "of": {
            "type": "integer",
            "description": "Total number of batches in the current phase."
          },
          "level": {
            "type": "integer",
            "description": "Dependency level (matches `dependencyLevel` from the topological sort)."
          }
        },
        "required": [
          "index",
          "of",
          "level"
        ]
      },
      "SyncJob": {
        "type": "object",
        "description": "Sync job header as surfaced by `getSyncJob` and the create response. The\ncanonical persistence shape is described in `docs/sync/INTERFACES.md`.\n",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SyncJobStatus"
          },
          "direction": {
            "$ref": "#/components/schemas/SyncDirection"
          },
          "source_org_id": {
            "type": "string"
          },
          "target_org_id": {
            "type": "string"
          },
          "dry_run": {
            "type": "boolean"
          },
          "counts": {
            "$ref": "#/components/schemas/SyncJobCounts"
          },
          "current_phase": {
            "$ref": "#/components/schemas/SyncPhase"
          },
          "current_batch": {
            "$ref": "#/components/schemas/SyncJobBatch"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          },
          "events": {
            "type": "array",
            "description": "Most recent events (capped server-side, typically last 10).",
            "items": {
              "$ref": "#/components/schemas/SyncJobEvent"
            }
          },
          "errors_sample": {
            "type": "array",
            "description": "Up to 20 sample error messages from failed resources.",
            "maxItems": 20,
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "source_id": {
                  "type": "string"
                },
                "error": {
                  "type": "string"
                }
              },
              "required": [
                "type",
                "source_id",
                "error"
              ]
            }
          }
        },
        "required": [
          "id",
          "status",
          "direction",
          "source_org_id",
          "target_org_id",
          "dry_run",
          "counts",
          "started_at"
        ]
      },
      "SyncJobListResponse": {
        "type": "object",
        "description": "Cursor-paginated list of sync jobs.",
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SyncJob"
            }
          }
        },
        "required": [
          "results"
        ]
      },
      "SyncJobResource": {
        "type": "object",
        "description": "Per-resource row backed by `SyncResourcesTable`. See\n`docs/sync/INTERFACES.md` for the DDB shape.\n",
        "properties": {
          "type": {
            "type": "string"
          },
          "source_id": {
            "type": "string"
          },
          "target_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/SyncJobResourceStatus"
          },
          "phase": {
            "$ref": "#/components/schemas/SyncPhase"
          },
          "attempt": {
            "type": "integer"
          },
          "error": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "type",
          "source_id",
          "status",
          "phase",
          "attempt",
          "updated_at"
        ]
      },
      "SyncJobResourceListResponse": {
        "type": "object",
        "description": "Cursor-paginated list of sync job resources.",
        "properties": {
          "next_cursor": {
            "type": "string"
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SyncJobResource"
            }
          }
        },
        "required": [
          "results"
        ]
      }
    }
  }
}
