openapi: 3.1.0
info:
  title: Tango Location Inbound API
  version: "2.0"
  description: |
    Async REST API: client PUTs Property / Building / Floor / Location-Store
    records into Tango. Tango disambiguates by the entity-type key fields in
    each record and writes to the corresponding tabs. Two-call pattern with
    gu_id polling.

    Direction NOTE: source doc's header lists `Source: TANGO / Destination:
    Client System` but the body says "Insert/Update ... in Tango" — clearly
    inbound. Treated as Client → Tango.

    Field schema NOTE: the source data-mapping table did not extract into the
    text dump (it lives in tables not parsed). The full field list is
    therefore unknown from this source — the schema lists only fields named
    in the validations (which are authoritative for required-ness). Mark this
    as a thin spec.
  x-direction: inbound
  x-source: Client System
  x-destination: Tango
servers:
  - url: https://api.tangoanalytics.com
    description: Production
  - url: https://testapi.tangoanalytics.com
    description: Test (SIT/UAT)
tags:
  - name: Locations
  - name: Status
paths:
  /v1/locationmgmt/locations:
    put:
      summary: Insert / update Property / Building / Floor / Location-Store records (async)
      operationId: putLocationsBatch
      tags: [Locations]
      parameters:
        - $ref: "#/components/parameters/XClient"
        - $ref: "#/components/parameters/XApiKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/LocationBatchRequest"
      responses:
        "200":
          description: Accepted for async processing.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedResponse"
  /v1/batch/status:
    get:
      summary: Poll processing status by gu_id
      operationId: getBatchStatus
      tags: [Status]
      parameters:
        - $ref: "#/components/parameters/XClient"
        - $ref: "#/components/parameters/XApiKey"
        - in: query
          name: gu_id
          required: true
          schema: { type: string, format: uuid }
      responses:
        "200":
          description: Status payload.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/StatusResponse"
components:
  securitySchemes:
    xClientHeader: { type: apiKey, in: header, name: x-client }
    xApiKeyHeader: { type: apiKey, in: header, name: x-api-key }
  parameters:
    XClient:
      name: x-client
      in: header
      required: true
      schema: { type: string }
    XApiKey:
      name: x-api-key
      in: header
      required: true
      schema: { type: string }
  schemas:
    LocationBatchRequest:
      type: object
      required: [records]
      properties:
        records:
          type: array
          items:
            $ref: "#/components/schemas/LocationRecord"
    LocationRecord:
      type: object
      description: |
        Permissive schema — the source design doc does not include a
        complete data-mapping table in the extracted text. Fields named in
        the validations are listed below; additional fields are accepted
        via additionalProperties.

        Identification: at least one of `store_id` / `store_number` must be
        present (validation #1).
      additionalProperties: true
      properties:
        store_id:
          type: string
          description: One of store_id / store_number is required.
        store_number:
          type: string
          description: One of store_id / store_number is required.
        store_number_erp: { type: string }
        store_number_legacy: { type: string }
        brand_id:
          type: string
          description: |
            Optional — if absent and client has only one brand, that brand
            is used. If absent and client has multiple brands, returns error.
        geo_code:
          type: string
          enum: [Y, N]
          description: |
            Y → Tango auto-populates Latitude/Longitude from the address.
            N or null → Tango uses provided coordinates or leaves blank.
        latitude:
          type: number
          description: |
            Required when geo_code is N/null AND the record is an INSERT
            (new store creation).
        longitude:
          type: number
          description: |
            Required when geo_code is N/null AND the record is an INSERT.
        status:
          type: string
          description: Required only for INSERT (creating a new store). Must match a status lookup.
        country:
          type: string
          description: |
            Required for INSERT when geo_code = Y. Must be ISO 3166-1
            alpha-3 (3-letter ISO country code).
        property_type: { type: string, description: "Lookup." }
        dev_type: { type: string, description: "Lookup." }
        facility_type: { type: string, description: "Lookup." }
        location_type: { type: string, description: "Lookup." }
        ownership_type: { type: string, description: "Lookup." }
        contractual_type: { type: string, description: "Lookup." }
        measurement_type: { type: string, description: "Lookup." }
        occupancy_status: { type: string, description: "Lookup." }
        store_size: { type: number }
        proposed_start_date: { type: string, description: "MM/DD/YYYY." }
        purchase_date: { type: string, description: "MM/DD/YYYY." }
        open_date: { type: string, description: "MM/DD/YYYY." }
        close_date: { type: string, description: "MM/DD/YYYY." }
        address: { type: string, description: "Required when geo_code = Y." }
        city: { type: string, description: "Required when geo_code = Y." }
        state: { type: string, description: "Required when geo_code = Y." }
        county: { type: string, description: "Required when geo_code = Y." }
        zip: { type: string, description: "Required when geo_code = Y." }
    AcceptedResponse:
      type: object
      required: [status, gu_id]
      properties:
        status: { type: string, enum: [OK, Error] }
        status_message: { type: string }
        gu_id: { type: string, format: uuid }
    StatusResponse:
      oneOf:
        - type: object
          properties:
            status: { type: string, const: PROCESSING }
        - type: object
          required: [status, total_records]
          properties:
            status: { type: string, const: COMPLETED }
            total_records: { type: integer }
            errors: { type: 'null' }
        - type: object
          required: [status, total_records, errors]
          properties:
            status: { type: string, const: ERROR }
            total_records: { type: integer }
            errors:
              type: array
              items:
                type: object
                required: [record_index, error]
                properties:
                  record_index: { type: integer }
                  record_key: { type: string }
                  error: { type: string }
security:
  - xClientHeader: []
    xApiKeyHeader: []
