openapi: 3.0.3 info: title: Lokalisa Public API version: 1.6.0 servers: - url: https://api.lokalisa.com - url: https://sandbox-api.lokalisa.com paths: /api/v1/auth/token: post: summary: Generar token parameters: - in: header name: X-API-Key required: true schema: { type: string } - in: header name: X-API-Secret required: true schema: { type: string } responses: '200': { description: OK } /api/v1/catalog/addresses: get: summary: Catalogo de direcciones pickup del negocio security: [{ BearerAuth: [] }] responses: '200': { description: OK } post: summary: Crear o actualizar idempotentemente una dirección pickup del negocio security: [{ BearerAuth: [] }] requestBody: required: true content: application/json: schema: type: object required: [external_id, name, address_line, city, state, postal_code, place_id, lat, lng] properties: external_id: { type: string, maxLength: 80 } name: { type: string, maxLength: 180 } address_line: { type: string, maxLength: 255 } city: { type: string } state: { type: string } postal_code: { type: string } phone: { type: [string, "null"] } place_id: { type: string } lat: { type: number, minimum: -90, maximum: 90 } lng: { type: number, minimum: -180, maximum: 180 } references: { type: [string, "null"] } notes: { type: [string, "null"] } address_components: { type: [object, "null"] } responses: '201': { description: Dirección creada } '200': { description: Dirección existente actualizada por external_id } '422': { description: Datos inválidos } /api/v1/coverage/zones: get: summary: Snapshot versionado de geocercas activas asignadas al negocio security: [{ BearerAuth: [] }] parameters: - in: header name: If-None-Match required: false schema: { type: string } responses: '200': { description: Snapshot con version, ETag y polígonos válidos } '304': { description: El snapshot no cambió } /api/v1/delivery-dates: get: summary: Fechas disponibles según las reglas operativas del negocio security: [{ BearerAuth: [] }] parameters: - in: query name: days schema: { type: integer, minimum: 1, maximum: 60, default: 30 } responses: '200': { description: Lista de fechas disponibles } /api/v1/quotes: post: summary: Cotizar envio security: [{ BearerAuth: [] }] requestBody: required: true content: application/json: schema: type: object required: [dropoff_lat, dropoff_lng] properties: pickup_address_id: { type: integer } distance_km: { type: number } dropoff_lat: { type: number } dropoff_lng: { type: number } delivery_date: { type: string, format: date } order_total_weight_kg: { type: number, minimum: 0, exclusiveMinimum: true } responses: '200': { description: OK } /api/v1/catalog/products: get: summary: Catalogo de productos con stock (FULL) security: [{ BearerAuth: [] }] responses: '200': { description: OK } /api/v1/catalog/products/{externalId}: put: summary: Crear o actualizar idempotentemente una variante de catálogo (sólo FULL) security: [{ BearerAuth: [] }] parameters: - in: path name: externalId required: true schema: { type: string, maxLength: 120 } requestBody: required: true content: application/json: schema: type: object required: [owner_external_id, store_external_ids, name, sku, price, weight_kg, is_active] properties: owner_external_id: { type: string, maxLength: 120 } store_external_ids: type: array minItems: 1 items: { type: string, maxLength: 120 } name: { type: string, maxLength: 255 } sku: { type: string, maxLength: 100, description: Identificador único usado en el almacén } source_sku: { type: [string, "null"], maxLength: 100, description: SKU visible del vendedor } barcode: { type: [string, "null"], maxLength: 120 } description: { type: [string, "null"] } price: { type: number, minimum: 0 } weight_kg: { type: number, minimum: 0, exclusiveMinimum: true } is_active: { type: boolean } dimensions: type: [object, "null"] properties: length_cm: { type: [number, "null"] } width_cm: { type: [number, "null"] } height_cm: { type: [number, "null"] } responses: '201': { description: Producto creado } '200': { description: Producto actualizado por external_id } '422': { description: La cuenta no es FULL o los datos son inválidos } /api/v1/orders: post: summary: Crear pedido security: [{ BearerAuth: [] }] parameters: - in: header name: Idempotency-Key required: true schema: { type: string } requestBody: required: true content: application/json: schema: type: object required: - external_id - pickup_address_id - items properties: external_id: { type: string } pickup_address_id: { type: integer } dropoff_name: { type: string } dropoff_phone: { type: string } dropoff_address: { type: string } dropoff_lat: { type: number } dropoff_lng: { type: number } delivery_date: { type: string, format: date } delivery_schedule: { type: string, enum: [NEXT_DAY, SCHEDULED] } order_total_weight_kg: { type: number, minimum: 0, exclusiveMinimum: true } shipping_charge_mode: { type: string, enum: [BUSINESS, CUSTOMER_FULL, SPLIT] } shipping_customer_amount_cap: { type: number, minimum: 0 } payment_mode: type: string enum: [ONLINE, COD, PAID] items: type: array minItems: 1 items: type: object required: [name, quantity] properties: product_id: { type: integer } name: { type: string } quantity: { type: integer } unit_price: { type: number } description: En negocios FULL, product_id es obligatorio y debe referenciar un producto activo del mismo negocio. responses: '201': description: Pedido creado y notificación WhatsApp encolada o marcada con error de configuración content: application/json: schema: type: object properties: ok: { type: boolean } notification: nullable: true type: object properties: id: { type: integer } channel: { type: string, enum: [whatsapp] } status: { type: string, enum: [queued, processing, accepted, failed] } recipient_confirmation: type: object properties: required: { type: boolean } status: { type: string, enum: [NOT_REQUIRED, PENDING, CONFIRMED, CANCELLED] } /api/v1/orders/{order}: get: summary: Consultar pedido, confirmación e historial WhatsApp security: [{ BearerAuth: [] }] parameters: - in: path name: order required: true schema: { type: integer } responses: '200': { description: OK } /api/v1/orders/{order}/cancel: post: summary: Cancelar pedido de forma idempotente security: [{ BearerAuth: [] }] parameters: - in: path name: order required: true schema: { type: integer } - in: header name: Idempotency-Key required: true schema: { type: string } responses: '200': { description: Pedido cancelado o ya cancelado } components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: token