{"openapi":"3.1.0","info":{"title":"SalesPro Hub External Integration API","version":"1.0.0","summary":"Field sales orders, inventory, CRM customers and Zapier REST hooks","description":"The public server-to-server integration contract for SalesPro Hub. Named ERP, accounting and CRM connections may require customer-specific middleware.","contact":{"name":"SalesPro Hub integration support","url":"https://salesrepsoftware.co.za/contact?subject=developer-support"}},"externalDocs":{"description":"SalesPro Hub developer documentation","url":"https://salesrepsoftware.co.za/developers"},"servers":[{"url":"https://portal.salesrepsoftware.co.za/api/v1","description":"Production"}],"tags":[{"name":"Orders","description":"Submitted order hand-off and fulfilment status"},{"name":"Inventory","description":"SKU stock reads, absolute sync and movement audit"},{"name":"Customers","description":"Externally identified CRM customer records"},{"name":"Zapier","description":"Developer-managed Zapier REST-hook subscriptions"}],"security":[{"ApiKey":[]},{"BearerApiKey":[]}],"paths":{"/orders":{"get":{"operationId":"listOrders","tags":["Orders"],"summary":"List submitted orders","description":"Draft orders are excluded. Use updated_since with updated_since_id for cursor-safe incremental polling.","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["pending","confirmed","processing","delivered","cancelled"]}},{"name":"type","in":"query","schema":{"type":"string","enum":["order","quote"]}},{"name":"acknowledged","in":"query","schema":{"type":"boolean"}},{"name":"updated_since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"updated_since_id","in":"query","schema":{"type":"integer","minimum":0}},{"name":"from_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"to_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"Paginated order collection with the next incremental cursor"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/orders/{orderNumber}":{"get":{"operationId":"getOrder","tags":["Orders"],"summary":"Retrieve one submitted order","parameters":[{"name":"orderNumber","in":"path","required":true,"description":"SalesPro Hub order number","schema":{"type":"string"}}],"responses":{"200":{"description":"Order, customer, sales representative and line-item detail"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"Order not found in the authenticated tenant"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/orders/{orderNumber}/acknowledge":{"post":{"operationId":"acknowledgeOrder","tags":["Orders"],"summary":"Acknowledge ERP receipt","description":"Retry-safe when the same external reference is repeated. A different reference for an already-linked order returns 409.","parameters":[{"name":"orderNumber","in":"path","required":true,"description":"SalesPro Hub order number","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"external_reference":{"type":["string","null"],"maxLength":255}}}}}},"responses":{"200":{"description":"Acknowledgement accepted or safely replayed"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"Order not found"},"409":{"description":"Order is linked to a different external reference"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/orders/{orderNumber}/status":{"put":{"operationId":"updateOrderStatus","tags":["Orders"],"summary":"Return fulfilment status","parameters":[{"name":"orderNumber","in":"path","required":true,"description":"SalesPro Hub order number","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["confirmed","processing","delivered","cancelled"]},"external_reference":{"type":["string","null"],"maxLength":255},"notes":{"type":["string","null"],"maxLength":1000}}}}}},"responses":{"200":{"description":"Status accepted or safely replayed"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"Order not found"},"409":{"description":"External reference conflicts with the linked reference"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/inventory":{"get":{"operationId":"listInventory","tags":["Inventory"],"summary":"List inventory","parameters":[{"name":"is_active","in":"query","schema":{"type":"boolean"}},{"name":"updated_since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":100}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"Products with on-hand, reserved and available quantities"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/inventory/movements":{"get":{"operationId":"listInventoryMovements","tags":["Inventory"],"summary":"Audit inventory movements","parameters":[{"name":"sku","in":"query","schema":{"type":"string","maxLength":255}},{"name":"type","in":"query","schema":{"type":"string","enum":["reservation","release","deduction","adjustment","import","sync"]}},{"name":"from_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"to_date","in":"query","schema":{"type":"string","format":"date"}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"Paginated stock movement audit trail"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/inventory/{sku}":{"get":{"operationId":"getInventoryItem","tags":["Inventory"],"summary":"Retrieve one SKU","parameters":[{"name":"sku","in":"path","required":true,"description":"Case-sensitive product SKU","schema":{"type":"string"}}],"responses":{"200":{"description":"Current on-hand, reserved and available quantity"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"Product not found"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}},"put":{"operationId":"setInventoryQuantity","tags":["Inventory"],"summary":"Set absolute stock for one SKU","parameters":[{"name":"sku","in":"path","required":true,"description":"Case-sensitive product SKU","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["stock_qty"],"properties":{"stock_qty":{"type":"integer","minimum":0},"reference":{"type":["string","null"],"maxLength":255}}}}}},"responses":{"200":{"description":"Quantity updated, unchanged or safely replayed"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"Product not found"},"409":{"description":"Reference was previously accepted with another quantity"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/inventory/bulk":{"post":{"operationId":"bulkSetInventory","tags":["Inventory"],"summary":"Set stock for up to 500 SKUs","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["products"],"properties":{"products":{"type":"array","minItems":1,"maxItems":500,"items":{"type":"object","required":["sku","stock_qty"],"properties":{"sku":{"type":"string","maxLength":255},"stock_qty":{"type":"integer","minimum":0}}}},"reference":{"type":["string","null"],"maxLength":255}}}}}},"responses":{"200":{"description":"Per-item updated, replayed and failed totals"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/customers":{"get":{"operationId":"listCustomers","tags":["Customers"],"summary":"List CRM customer records","description":"Incremental requests include deletion tombstones by default. Persist both cursor fields.","parameters":[{"name":"updated_since","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"updated_since_id","in":"query","schema":{"type":"integer","minimum":0}},{"name":"is_active","in":"query","schema":{"type":"boolean"}},{"name":"include_deleted","in":"query","schema":{"type":"boolean"}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}}],"responses":{"200":{"description":"Paginated customer collection with next cursor"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/customers/{externalSystem}/{externalId}":{"get":{"operationId":"getCustomerByExternalIdentity","tags":["Customers"],"summary":"Retrieve a CRM customer or tombstone","parameters":[{"name":"externalSystem","in":"path","required":true,"description":"Source-system key such as hubspot or sage","schema":{"type":"string"}},{"name":"externalId","in":"path","required":true,"description":"Immutable identifier assigned by the source system","schema":{"type":"string"}}],"responses":{"200":{"description":"Customer or deletion tombstone"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"External identity not found"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}},"put":{"operationId":"upsertCustomer","tags":["Customers"],"summary":"Idempotently upsert a CRM customer","parameters":[{"name":"externalSystem","in":"path","required":true,"description":"Source-system key","schema":{"type":"string"}},{"name":"externalId","in":"path","required":true,"description":"Immutable source-system identifier","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","maxLength":255},"customer_code":{"type":["string","null"],"maxLength":50},"contact_person":{"type":["string","null"],"maxLength":255},"email":{"type":["string","null"],"format":"email","maxLength":255},"phone":{"type":["string","null"],"maxLength":50},"address":{"type":["string","null"],"maxLength":500},"city":{"type":["string","null"],"maxLength":100},"province":{"type":["string","null"],"maxLength":100},"postal_code":{"type":["string","null"],"maxLength":20},"lat":{"type":["number","null"],"minimum":-90,"maximum":90},"lng":{"type":["number","null"],"minimum":-180,"maximum":180},"metadata":{"type":["object","null"],"additionalProperties":true},"is_active":{"type":"boolean"},"create_default_store":{"type":"boolean","default":true}}}}}},"responses":{"200":{"description":"Existing customer updated or reactivated"},"201":{"description":"Customer created"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}},"delete":{"operationId":"deactivateCustomer","tags":["Customers"],"summary":"Deactivate a CRM customer","description":"Retains the external identity so a later upsert can reactivate the record.","parameters":[{"name":"externalSystem","in":"path","required":true,"description":"Source-system key","schema":{"type":"string"}},{"name":"externalId","in":"path","required":true,"description":"Immutable source-system identifier","schema":{"type":"string"}}],"responses":{"200":{"description":"Customer deactivated or deactivation safely replayed"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"404":{"description":"External identity not found"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/zapier/subscribe":{"post":{"operationId":"subscribeZapierHook","tags":["Zapier"],"summary":"Subscribe a Zapier catch hook","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["event","target_url"],"properties":{"event":{"type":"string","enum":["order.created","order.delivered","customer.created","check_in.completed"]},"target_url":{"type":"string","format":"uri","maxLength":500}}}}}},"responses":{"200":{"description":"Existing subscription reactivated"},"201":{"description":"Subscription created"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/zapier/subscribe/{subscription}":{"delete":{"operationId":"unsubscribeZapierHook","tags":["Zapier"],"summary":"Remove a Zapier subscription","parameters":[{"name":"subscription","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Subscription removed"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/zapier/subscriptions":{"get":{"operationId":"listZapierSubscriptions","tags":["Zapier"],"summary":"List tenant Zapier subscriptions","responses":{"200":{"description":"Subscription collection"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}},"/zapier/sample":{"get":{"operationId":"getZapierSample","tags":["Zapier"],"summary":"Get sample data for one trigger","parameters":[{"name":"event","in":"query","required":true,"schema":{"type":"string","enum":["order.created","order.delivered","customer.created","check_in.completed"]}}],"responses":{"200":{"description":"One representative sample in the data array"},"401":{"description":"Missing, invalid, disabled or expired API key"},"402":{"description":"Organisation subscription is not active"},"403":{"description":"API feature or required scope is unavailable"},"422":{"description":"Request validation failed"},"429":{"description":"Per-minute throttle or daily tenant quota exceeded"}}}}},"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Recommended server-to-server authentication header"},"BearerApiKey":{"type":"http","scheme":"bearer","bearerFormat":"SalesPro Hub API key"}}}}