{
  "openapi": "3.0.3",
  "info": {
    "title": "Ordinavo Connect API",
    "version": "v1",
    "description": "Public Ordinavo Connect API for integration health checks, inbound target imports, batch imports, ImportJob monitoring and route suggestions from imported jobs."
  },
  "servers": [
    {
      "url": "https://ordinavo.de/api/connect/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "OrdinavoApiKey": []
    }
  ],
  "tags": [
    { "name": "Integration Health" },
    { "name": "Inbound Targets" },
    { "name": "Batch Import" },
    { "name": "Import Jobs" },
    { "name": "Route Suggestions" }
  ],
  "paths": {
    "/integration/health": {
      "get": {
        "tags": ["Integration Health"],
        "summary": "Check integration authentication",
        "description": "Required scope: integration.health.read",
        "responses": {
          "200": {
            "description": "Authentication works",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/IntegrationHealthResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" },
          "429": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/inbound/targets": {
      "post": {
        "tags": ["Inbound Targets"],
        "summary": "Import one target",
        "description": "Required scope: inbound.targets.write. Add customers.map to link incoming customer, location and contact records to existing customer references.",
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/InboundTargetDto" }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/ImportJobResponse" },
          "201": { "$ref": "#/components/responses/ImportJobResponse" },
          "400": { "$ref": "#/components/responses/ErrorResponse" },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" },
          "409": { "$ref": "#/components/responses/ErrorResponse" },
          "429": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/inbound/targets/batch": {
      "post": {
        "tags": ["Batch Import"],
        "summary": "Import multiple targets",
        "description": "Required scope: inbound.targets.batch.write. Items are processed independently and may partially fail.",
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/InboundTargetBatchDto" }
            }
          }
        },
        "responses": {
          "200": { "$ref": "#/components/responses/ImportJobResponse" },
          "400": { "$ref": "#/components/responses/ErrorResponse" },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" },
          "409": { "$ref": "#/components/responses/ErrorResponse" },
          "413": { "$ref": "#/components/responses/ErrorResponse" },
          "429": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/import-jobs": {
      "get": {
        "tags": ["Import Jobs"],
        "summary": "List import jobs",
        "description": "Required scope: import_jobs.read",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "sourceSystem",
            "in": "query",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "skip",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 0, "default": 0 }
          },
          {
            "name": "take",
            "in": "query",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Paged import jobs",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ImportJobListResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/import-jobs/{id}": {
      "get": {
        "tags": ["Import Jobs"],
        "summary": "Get import job details",
        "description": "Required scope: import_jobs.read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer", "format": "int64" }
          }
        ],
        "responses": {
          "200": { "$ref": "#/components/responses/ImportJobDetailsResponse" },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" },
          "404": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/import-jobs/{id}/route-suggestion": {
      "post": {
        "tags": ["Route Suggestions"],
        "summary": "Create draft route suggestion from an import job",
        "description": "Required scope: route_suggestions.create. Creates a draft only; a manager reviews and publishes routes in Ordinavo.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer", "format": "int64" }
          },
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateImportJobRouteSuggestionApiRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Route suggestion created",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CreateRouteSuggestionResponse" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/ErrorResponse" },
          "403": { "$ref": "#/components/responses/ErrorResponse" },
          "404": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "OrdinavoApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Ordinavo Connect API Key",
        "description": "Use a Ordinavo Connect API key generated for an Integration Client. Example: Authorization: Bearer YOUR_API_KEY"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": { "type": "string" },
        "description": "Recommended for all write requests. Reusing the same key with the same payload returns the original response."
      }
    },
    "responses": {
      "ImportJobResponse": {
        "description": "Import job result",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/InboundImportResultDto" }
          }
        }
      },
      "ImportJobDetailsResponse": {
        "description": "Import job details",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ImportJobDetailsDto" }
          }
        }
      },
      "ErrorResponse": {
        "description": "Standard error response",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ApiErrorResponse" }
          }
        }
      }
    },
    "schemas": {
      "IntegrationHealthResponse": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "example": "ok" },
          "tenantId": { "type": "string", "example": "tenant_123" },
          "clientId": { "type": "string", "example": "ic_logistics_erp" },
          "sourceSystem": { "type": "string", "example": "logistics_erp" },
          "serverTimeUtc": { "type": "string", "format": "date-time" }
        }
      },
      "InboundTargetDto": {
        "type": "object",
        "required": ["externalId", "title"],
        "properties": {
          "sourceSystem": { "type": "string", "nullable": true, "example": "logistics_erp" },
          "externalId": { "type": "string", "example": "JOB-2026-000918" },
          "externalType": { "type": "string", "nullable": true, "example": "ServiceJob" },
          "operation": { "type": "string", "enum": ["create", "update", "upsert", "cancel"], "default": "upsert" },
          "templateKey": { "type": "string", "nullable": true },
          "title": { "type": "string", "example": "Maintenance visit" },
          "description": { "type": "string", "nullable": true },
          "customer": { "$ref": "#/components/schemas/InboundTargetCustomerDto" },
          "location": { "$ref": "#/components/schemas/InboundTargetLocationDto" },
          "contact": { "$ref": "#/components/schemas/InboundTargetContactDto" },
          "address": { "$ref": "#/components/schemas/InboundTargetAddressDto" },
          "planning": { "$ref": "#/components/schemas/InboundTargetPlanningDto" },
          "businessValue": { "$ref": "#/components/schemas/InboundTargetBusinessValueDto" },
          "customFields": {
            "type": "object",
            "additionalProperties": { "type": "string", "nullable": true }
          }
        }
      },
      "InboundTargetCustomerDto": {
        "type": "object",
        "description": "Customer context for mapping imported jobs. customerNumber is stored on the Ordinavo customer record and never on the target.",
        "properties": {
          "externalId": { "type": "string", "nullable": true, "example": "CUST-10042" },
          "customerNumber": { "type": "string", "nullable": true, "example": "RRM-001" },
          "name": { "type": "string", "nullable": true, "example": "Rhein-Ruhr Medical Supply" },
          "type": { "type": "string", "nullable": true, "example": "Business" },
          "industry": { "type": "string", "nullable": true, "example": "Medical Supply" },
          "email": { "type": "string", "nullable": true, "format": "email" },
          "phone": { "type": "string", "nullable": true },
          "website": { "type": "string", "nullable": true, "format": "uri" }
        }
      },
      "InboundTargetLocationDto": {
        "type": "object",
        "properties": {
          "externalId": { "type": "string", "nullable": true, "example": "LOC-ESSEN-01" },
          "name": { "type": "string", "nullable": true, "example": "Essen Center" },
          "type": { "type": "string", "nullable": true, "example": "Branch" },
          "street": { "type": "string", "nullable": true },
          "postalCode": { "type": "string", "nullable": true },
          "city": { "type": "string", "nullable": true },
          "region": { "type": "string", "nullable": true },
          "country": { "type": "string", "nullable": true, "example": "DE" },
          "latitude": { "type": "number", "format": "double", "nullable": true },
          "longitude": { "type": "number", "format": "double", "nullable": true },
          "defaultEstimatedWorkMinutes": { "type": "integer", "nullable": true, "minimum": 5, "maximum": 480 },
          "defaultPriority": { "type": "string", "nullable": true, "example": "High" },
          "accessNotes": { "type": "string", "nullable": true },
          "parkingNotes": { "type": "string", "nullable": true }
        }
      },
      "InboundTargetContactDto": {
        "type": "object",
        "properties": {
          "externalId": { "type": "string", "nullable": true, "example": "CONT-44" },
          "firstName": { "type": "string", "nullable": true },
          "lastName": { "type": "string", "nullable": true },
          "displayName": { "type": "string", "nullable": true, "example": "Anna Weber" },
          "role": { "type": "string", "nullable": true, "example": "Operations Manager" },
          "department": { "type": "string", "nullable": true },
          "email": { "type": "string", "format": "email", "nullable": true },
          "phone": { "type": "string", "nullable": true },
          "mobile": { "type": "string", "nullable": true },
          "preferredContactMethod": { "type": "string", "nullable": true, "example": "Phone" }
        }
      },
      "InboundTargetAddressDto": {
        "type": "object",
        "properties": {
          "street": { "type": "string", "nullable": true },
          "postalCode": { "type": "string", "nullable": true },
          "city": { "type": "string", "nullable": true },
          "country": { "type": "string", "nullable": true, "example": "DE" },
          "latitude": { "type": "number", "format": "double", "nullable": true },
          "longitude": { "type": "number", "format": "double", "nullable": true }
        }
      },
      "InboundTargetPlanningDto": {
        "type": "object",
        "properties": {
          "routeDate": { "type": "string", "format": "date", "nullable": true },
          "dueAtUtc": { "type": "string", "format": "date-time", "nullable": true },
          "timeWindowStartUtc": { "type": "string", "format": "date-time", "nullable": true },
          "timeWindowEndUtc": { "type": "string", "format": "date-time", "nullable": true },
          "estimatedWorkMinutes": { "type": "integer", "minimum": 5, "maximum": 480, "nullable": true },
          "priority": { "type": "string", "nullable": true, "example": "High" },
          "fixedTime": { "type": "boolean", "nullable": true }
        }
      },
      "InboundTargetBusinessValueDto": {
        "type": "object",
        "properties": {
          "expectedRevenue": { "type": "number", "nullable": true },
          "customerValue": { "type": "number", "nullable": true },
          "closeProbability": { "type": "number", "nullable": true },
          "customerTier": { "type": "string", "nullable": true },
          "weight": { "type": "integer", "nullable": true }
        }
      },
      "InboundTargetBatchDto": {
        "type": "object",
        "required": ["targets"],
        "properties": {
          "sourceSystem": { "type": "string", "nullable": true },
          "batchId": { "type": "string", "nullable": true },
          "operation": { "type": "string", "enum": ["create", "update", "upsert", "cancel"], "default": "upsert" },
          "targets": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/InboundTargetDto" }
          }
        }
      },
      "InboundImportResultDto": {
        "type": "object",
        "properties": {
          "importJobId": { "type": "integer", "format": "int64" },
          "status": { "type": "string", "example": "Accepted" },
          "totalItems": { "type": "integer" },
          "createdItems": { "type": "integer" },
          "updatedItems": { "type": "integer" },
          "cancelledItems": { "type": "integer" },
          "rejectedItems": { "type": "integer" },
          "warningItems": { "type": "integer" },
          "items": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/InboundImportItemResultDto" }
          },
          "links": {
            "type": "object",
            "additionalProperties": { "type": "string" }
          }
        }
      },
      "InboundImportItemResultDto": {
        "type": "object",
        "properties": {
          "externalId": { "type": "string" },
          "status": { "type": "string", "example": "Imported" },
          "targetId": { "type": "integer", "nullable": true },
          "errorCode": { "type": "string", "nullable": true },
          "errorMessage": { "type": "string", "nullable": true },
          "warnings": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/InboundImportWarningDto" }
          },
          "customerMapping": { "$ref": "#/components/schemas/InboundCustomerMappingResultDto" }
        }
      },
      "InboundImportWarningDto": {
        "type": "object",
        "properties": {
          "code": { "type": "string" },
          "message": { "type": "string" }
        }
      },
      "InboundCustomerMappingResultDto": {
        "type": "object",
        "properties": {
          "status": { "$ref": "#/components/schemas/CustomerMappingStatus" },
          "customerId": { "type": "integer", "nullable": true },
          "customerLocationId": { "type": "integer", "nullable": true },
          "customerContactId": { "type": "integer", "nullable": true },
          "warnings": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CustomerMappingWarning" }
          },
          "errors": {
            "type": "array",
            "items": { "type": "string" }
          }
        }
      },
      "CustomerMappingStatus": {
        "type": "string",
        "enum": ["NotApplicable", "Mapped", "Created", "Updated", "Warning", "Failed", "Skipped"]
      },
      "CustomerMappingWarning": {
        "type": "string",
        "enum": [
          "LocationMissingCoordinates",
          "AmbiguousCustomerNumber",
          "TargetAddressDiffersFromCustomerLocation",
          "CustomerAutoCreateDisabled",
          "LocationAutoCreateDisabled",
          "ContactAutoCreateDisabled",
          "ContactLocationMismatch",
          "CustomerArchivedOrBlocked"
        ]
      },
      "ImportJobListResponse": {
        "type": "object",
        "properties": {
          "total": { "type": "integer" },
          "skip": { "type": "integer" },
          "take": { "type": "integer" },
          "jobs": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ImportJobSummaryDto" }
          }
        }
      },
      "ImportJobSummaryDto": {
        "type": "object",
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "sourceSystem": { "type": "string" },
          "batchId": { "type": "string", "nullable": true },
          "status": { "type": "string" },
          "totalItems": { "type": "integer" },
          "createdItems": { "type": "integer" },
          "updatedItems": { "type": "integer" },
          "cancelledItems": { "type": "integer" },
          "rejectedItems": { "type": "integer" },
          "warningItems": { "type": "integer" },
          "createdAtUtc": { "type": "string", "format": "date-time" },
          "processedAtUtc": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "ImportJobDetailsDto": {
        "allOf": [
          { "$ref": "#/components/schemas/ImportJobSummaryDto" },
          {
            "type": "object",
            "properties": {
              "items": {
                "type": "array",
                "items": { "$ref": "#/components/schemas/InboundImportItemResultDto" }
              }
            }
          }
        ]
      },
      "CreateImportJobRouteSuggestionApiRequest": {
        "type": "object",
        "required": ["routeDate"],
        "properties": {
          "routeDate": { "type": "string", "format": "date", "example": "2026-07-01" },
          "employeeId": { "type": "string", "nullable": true },
          "optimizationProfileId": { "type": "integer", "nullable": true },
          "minimumBufferMinutes": { "type": "integer", "nullable": true },
          "maxStops": { "type": "integer", "nullable": true, "example": 8 }
        }
      },
      "CreateRouteSuggestionResponse": {
        "type": "object",
        "properties": {
          "routeSuggestionId": { "type": "integer" },
          "status": { "type": "string", "example": "Draft" },
          "includedTargets": { "type": "integer" },
          "excludedTargets": { "type": "integer" },
          "links": {
            "type": "object",
            "additionalProperties": { "type": "string" }
          }
        }
      },
      "ApiErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": { "type": "string" },
              "message": { "type": "string" },
              "requiredScope": { "type": "string", "nullable": true },
              "retryAfterSeconds": { "type": "integer", "nullable": true },
              "correlationId": { "type": "string", "nullable": true }
            }
          }
        }
      }
    }
  }
}
