{
  "openapi": "3.1.0",
  "info": {
    "title": "Struvera Integration API",
    "version": "1.0.0",
    "summary": "Read the architecture model and push events from ITSM and work-tracker tools.",
    "description": "Token-authenticated API for integration platforms (for example Workato), ITSM tools such as ServiceDesk Plus or ServiceNow, and work trackers such as Azure DevOps or Jira. Create tokens in the workspace page (admins only). Tokens work only under /api/v1; session cookies are not accepted there. Reads stay available when a trial has expired; writes follow the same rule as model edits.",
    "license": { "name": "Proprietary", "identifier": "LicenseRef-Proprietary" }
  },
  "servers": [{ "url": "/api", "description": "This deployment" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Model", "description": "Read the tenant model." },
    { "name": "Events", "description": "Incidents, changes and work items mapped to systems." }
  ],
  "paths": {
    "/v1/model": {
      "get": {
        "tags": ["Model"], "operationId": "getModel", "summary": "Whole model document",
        "description": "Requires a read or write token.",
        "responses": {
          "200": { "description": "The tenant model.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Model" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/v1/capabilities": { "get": { "tags": ["Model"], "operationId": "listCapabilities", "summary": "Capabilities sheet rows", "responses": { "200": { "$ref": "#/components/responses/SheetRows" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } },
    "/v1/systems": { "get": { "tags": ["Model"], "operationId": "listSystems", "summary": "Systems sheet rows", "responses": { "200": { "$ref": "#/components/responses/SheetRows" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } },
    "/v1/data-objects": { "get": { "tags": ["Model"], "operationId": "listDataObjects", "summary": "DataObjects sheet rows", "responses": { "200": { "$ref": "#/components/responses/SheetRows" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } },
    "/v1/integrations": { "get": { "tags": ["Model"], "operationId": "listIntegrations", "summary": "Integrations sheet rows", "responses": { "200": { "$ref": "#/components/responses/SheetRows" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } },
    "/v1/projects": { "get": { "tags": ["Model"], "operationId": "listProjects", "summary": "Projects sheet rows", "responses": { "200": { "$ref": "#/components/responses/SheetRows" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "429": { "$ref": "#/components/responses/RateLimited" } } } },
    "/v1/events": {
      "get": {
        "tags": ["Events"], "operationId": "listEvents", "summary": "List events",
        "description": "Newest first by occurred_at. status=open returns events whose status is not closed, resolved, done or cancelled (case-insensitive).",
        "parameters": [
          { "name": "system_id", "in": "query", "schema": { "type": "string" }, "description": "Only events for this system." },
          { "name": "status", "in": "query", "schema": { "type": "string" }, "description": "open, all, or an exact status (case-insensitive). Default all." },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 100 } }
        ],
        "responses": {
          "200": { "description": "Events.", "content": { "application/json": { "schema": { "type": "object", "required": ["events", "count"], "properties": { "events": { "type": "array", "items": { "$ref": "#/components/schemas/Event" } }, "count": { "type": "integer" } } } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      },
      "post": {
        "tags": ["Events"], "operationId": "upsertEvents", "summary": "Upsert events",
        "description": "Requires a write token. Send one event object or {events:[...]} with up to 100. Each event is upserted on (source, external_id). An unknown system_id rejects that item. Responds 200 if any item was accepted, 422 if none. A workspace holds at most 5,000 open events.",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "oneOf": [{ "$ref": "#/components/schemas/EventInput" }, { "$ref": "#/components/schemas/EventBatch" }] } } } },
        "responses": {
          "200": { "description": "At least one item accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResults" } } } },
          "400": { "description": "Malformed body or more than 100 events.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "description": "Trial ended; reactivate to write.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "403": { "description": "The token is read-only.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "413": { "description": "Body over 1 MB.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
          "422": { "description": "No item was accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EventResults" } } } },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "API token created in the workspace page (sv_...). Shown once at creation." }
    },
    "responses": {
      "Unauthorized": { "description": "Missing, invalid or revoked token.", "headers": { "WWW-Authenticate": { "schema": { "type": "string" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "RateLimited": { "description": "More than 600 requests a minute for this token.", "headers": { "Retry-After": { "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "SheetRows": { "description": "Rows of one model sheet.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SheetRows" } } } }
    },
    "schemas": {
      "Error": { "type": "object", "required": ["error"], "properties": { "error": { "type": "string", "description": "Machine-readable code." }, "message": { "type": "string" } } },
      "Model": { "type": "object", "required": ["data", "updatedAt"], "properties": { "data": { "type": "object", "description": "Sheets keyed by name, each an array of row objects.", "additionalProperties": { "type": "array", "items": { "type": "object", "additionalProperties": true } } }, "updatedAt": { "type": ["integer", "null"], "description": "Epoch milliseconds of the last save." } } },
      "SheetRows": { "type": "object", "required": ["sheet", "count", "rows", "updatedAt"], "properties": { "sheet": { "type": "string" }, "count": { "type": "integer" }, "rows": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "updatedAt": { "type": ["integer", "null"] } } },
      "EventInput": {
        "type": "object", "required": ["source", "external_id", "type", "system_id"],
        "properties": {
          "source": { "type": "string", "maxLength": 120, "description": "Originating tool, for example servicedesk-plus." },
          "external_id": { "type": "string", "maxLength": 120, "description": "The record id in the source tool." },
          "type": { "type": "string", "enum": ["incident", "change", "work_item"] },
          "system_id": { "type": "string", "description": "Must exist in the workspace model Systems sheet." },
          "capability_id": { "type": ["string", "null"], "maxLength": 120 },
          "title": { "type": "string", "maxLength": 300 },
          "status": { "type": "string", "maxLength": 40, "default": "open" },
          "priority": { "type": ["string", "null"], "maxLength": 40 },
          "occurred_at": { "description": "ISO 8601 string or epoch milliseconds. Defaults to now.", "oneOf": [{ "type": "string", "format": "date-time" }, { "type": "integer" }] }
        }
      },
      "EventBatch": { "type": "object", "required": ["events"], "properties": { "events": { "type": "array", "minItems": 1, "maxItems": 100, "items": { "$ref": "#/components/schemas/EventInput" } } } },
      "Event": {
        "type": "object",
        "properties": {
          "source": { "type": "string" }, "external_id": { "type": "string" }, "type": { "type": "string", "enum": ["incident", "change", "work_item"] },
          "system_id": { "type": "string" }, "capability_id": { "type": ["string", "null"] }, "title": { "type": ["string", "null"] },
          "status": { "type": "string" }, "priority": { "type": ["string", "null"] },
          "occurred_at": { "type": "integer" }, "updated_at": { "type": "integer" }, "created_at": { "type": "integer" }
        }
      },
      "EventResults": {
        "type": "object", "required": ["results", "accepted", "rejected"],
        "properties": {
          "results": { "type": "array", "items": { "type": "object", "required": ["external_id", "ok"], "properties": { "external_id": { "type": ["string", "null"] }, "ok": { "type": "boolean" }, "error": { "type": "string" } } } },
          "accepted": { "type": "integer" }, "rejected": { "type": "integer" }
        }
      }
    }
  }
}
