Skip to main content

Supplier API

The Supplier API provides endpoints for managing suppliers and their relations to stores, groups, addresses, and contacts in optical stores.
All Supplier API endpoints require authentication and a valid x-store-id header to identify the store context, unless stated otherwise.

Overview

Suppliers can be:
  • Linked to one or more stores
  • Assigned to one or more supplier groups
  • Created with optional address and contact information
  • Assigned or updated with a default price list
  • Filtered and sorted by various criteria
  • Retrieved in paginated lists
  • Deleted individually or in bulk

Endpoint Details

Base URL: https://joptic.jethings.com
Content-Type: application/json

Required Headers

x-store-id: <store_id>
Authorization: Bearer <token>
All routes require a valid x-store-id header. Users must have an active relation with the store to access its suppliers.

Get All Suppliers

GET /suppliers Retrieve a paginated list of suppliers linked to the store that the authenticated user has access to. Requires Authentication: Bearer token in Authorization header

Required Headers

KeyValueRequiredDescription
x-store-idstringYesStore identifier (used by @StoreId() decorator)

Query Parameters

ParameterTypeRequiredDescriptionDefault
searchstringNoFilter by name or description (partial match)
namestringNoFilter by supplier name (partial match)
isActivebooleanNoFilter by active/inactive status
pagenumberNoPage number (1-based)1
limitnumberNoNumber of items per page (1–100)10
sortBystringNoSort field (name, isActive, updatedAt, createdAt)createdAt
sortOrderstringNoSort direction (asc or desc)desc

Success Response

Status Code: 200 OK
{
  "data": [
    {
      "id": "sup_12345",
      "storeIds": ["str_1", "str_2"],
      "supplierGroups": [
        { "id": "grp_1", "name": "Preferred" }
      ],
      "name": "Global Traders Ltd.",
      "description": "Household goods wholesaler",
      "note": "Ships on Mondays",
      "defaultPriceListId": "pl_abc123",
      "address": {
        "id": "addr_1",
        "street": "123 Main St",
        "city": "Casablanca",
        "state": "CA",
        "postalCode": "20000",
        "country": "MA"
      },
      "contact": {
        "id": "cont_1",
        "phone": "+212600111222",
        "fax": null,
        "email": "[email protected]",
        "website": "https://globaltraders.com"
      },
      "isActive": true,
      "createdAt": "2025-11-03T10:00:00.000Z",
      "updatedAt": "2025-11-03T10:30:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 12,
    "totalPages": 2,
    "hasNext": true,
    "hasPrev": false
  }
}

Response Fields

Supplier Object

FieldTypeDescription
idstringUnique supplier ID
storeIdsstring[]Store IDs this supplier is linked to
supplierGroupsobject[]Groups assigned to the supplier
supplierGroups[].idstringGroup ID
supplierGroups[].namestringGroup name
namestringSupplier display name
descriptionstring?Supplier description
notestring?Internal note
defaultPriceListIdstring?Default price list ID
addressAddress?Address information
contactContact?Contact information
isActivebooleanActive status
createdAtstringCreation timestamp (ISO 8601)
updatedAtstringLast update timestamp (ISO 8601)

Address

FieldTypeDescription
idstringAddress ID
streetstringStreet
citystringCity
statestringState/Province
postalCodestringPostal/ZIP code
countrystringCountry

Contact

FieldTypeDescription
idstringContact ID
phonestringPhone number
faxstringFax number
emailstringEmail address
websitestringWebsite URL

Pagination Object

FieldTypeDescription
pagenumberCurrent page number
limitnumberNumber of items per page
totalnumberTotal number of records
totalPagesnumberTotal pages available
hasNextbooleanWhether a next page exists
hasPrevbooleanWhether a previous page exists

Possible Errors

CodeMessage
403Forbidden - User does not have access to store
500Internal Server Error

Example Requests

curl -X GET "https://joptic.jethings.com/suppliers?page=1&limit=10" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"
Search suppliers by name/description:
curl -X GET "https://joptic.jethings.com/suppliers?search=global&page=1&limit=10&sortBy=name&sortOrder=asc" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"
Filter by active status:
curl -X GET "https://joptic.jethings.com/suppliers?isActive=true&page=1&limit=20" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"

Get Supplier by ID

GET /suppliers/:id Retrieve a single supplier by ID. The supplier must be linked to the current store. Requires Authentication: Bearer token in Authorization header

Required Headers

KeyValueRequiredDescription
x-store-idstringYesStore identifier (used by @StoreId() decorator)

Path Parameters

ParameterTypeRequiredDescription
idstringYesThe ID of the supplier

Success Response

Status Code: 200 OK
{
  "id": "sup_12345",
  "storeIds": ["str_1"],
  "supplierGroups": [{ "id": "grp_1", "name": "Preferred" }],
  "name": "Global Traders Ltd.",
  "description": "Household goods wholesaler",
  "note": "Ships on Mondays",
  "defaultPriceListId": "pl_abc123",
  "address": {
    "id": "addr_1",
    "street": "123 Main St",
    "city": "Casablanca",
    "state": "CA",
    "postalCode": "20000",
    "country": "MA"
  },
  "contact": {
    "id": "cont_1",
    "phone": "+212600111222",
    "fax": null,
    "email": "[email protected]",
    "website": "https://globaltraders.com"
  },
  "isActive": true,
  "createdAt": "2025-11-03T10:00:00.000Z",
  "updatedAt": "2025-11-03T10:30:00.000Z"
}

Possible Errors

CodeMessage
403Forbidden - User does not have access to store or supplier
404Not Found - Supplier not found or inaccessible
500Internal Server Error

Example Requests

curl -X GET "https://joptic.jethings.com/suppliers/sup_12345" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"

Create Supplier

POST /suppliers Create a new supplier and link it to the current store. You can optionally assign supplier groups, a default price list, address, and contact information. Requires Authentication: Bearer token in Authorization header

Required Headers

KeyValueRequiredDescription
x-store-idstringYesStore identifier (used by @StoreId() decorator)

Request Body

FieldTypeRequiredDescription
supplierGroupIdsstring[]NoSupplier groups to associate
namestring✅ YesSupplier name (max 255 characters)
descriptionstringNoDescription (max 1000 characters)
notestringNoInternal note (max 1000 characters)
defaultPriceListIdstringNoDefault price list to assign
addressAddressInputNoAddress to create
contactContactInputNoContact to create

AddressInput

FieldTypeRequiredDescription
streetstring✅ YesStreet (max 255 characters)
citystring✅ YesCity (max 100 characters)
statestring✅ YesState/Province (max 100 characters)
postalCodestring✅ YesPostal/ZIP code (max 20 characters)
countrystring✅ YesCountry (max 100 characters)

ContactInput

FieldTypeRequiredDescription
phonestringNoPhone number (max 20 characters)
faxstringNoFax number (max 20 characters)
emailstringNoEmail address (max 255 characters, must be valid email)
websitestringNoWebsite URL (max 255 characters)

Request Body Example

{
  "supplierGroupIds": ["grp_1", "grp_2"],
  "name": "Global Traders Ltd.",
  "description": "Household goods wholesaler",
  "note": "Ships on Mondays",
  "defaultPriceListId": "pl_abc123",
  "address": {
    "street": "123 Main St",
    "city": "Casablanca",
    "state": "CA",
    "postalCode": "20000",
    "country": "MA"
  },
  "contact": {
    "phone": "+212600111222",
    "fax": null,
    "email": "[email protected]",
    "website": "https://globaltraders.com"
  }
}

Success Response

Status Code: 200 OK
{
  "id": "sup_abc123",
  "storeIds": ["str_12345"],
  "supplierGroups": [
    { "id": "grp_1", "name": "Preferred" },
    { "id": "grp_2", "name": "Local" }
  ],
  "name": "Global Traders Ltd.",
  "description": "Household goods wholesaler",
  "note": "Ships on Mondays",
  "defaultPriceListId": "pl_abc123",
  "address": {
    "id": "addr_1",
    "street": "123 Main St",
    "city": "Casablanca",
    "state": "CA",
    "postalCode": "20000",
    "country": "MA"
  },
  "contact": {
    "id": "cont_1",
    "phone": "+212600111222",
    "fax": null,
    "email": "[email protected]",
    "website": "https://globaltraders.com"
  },
  "isActive": true,
  "createdAt": "2025-11-03T10:00:00.000Z",
  "updatedAt": "2025-11-03T10:00:00.000Z"
}

Possible Errors

CodeMessage
400Bad Request - Invalid request data
403Forbidden - User does not have access to this store
404Not Found - Price list or supplier group not found
500Internal Server Error - Failed to create supplier

Example Requests

curl -X POST "https://joptic.jethings.com/suppliers" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "supplierGroupIds": ["grp_1"],
    "name": "Global Traders Ltd.",
    "description": "Household goods wholesaler",
    "note": "Ships on Mondays",
    "defaultPriceListId": "pl_abc123",
    "address": {
      "street": "123 Main St",
      "city": "Casablanca",
      "state": "CA",
      "postalCode": "20000",
      "country": "MA"
    },
    "contact": {
      "phone": "+212600111222",
      "email": "[email protected]",
      "website": "https://globaltraders.com"
    }
  }'
When creating a supplier, the system automatically:
  1. Creates an address record if address information is provided
  2. Creates a contact record if contact information is provided
  3. Links the supplier to the authenticated user’s store
  4. Assigns the supplier to specified supplier groups if supplierGroupIds is provided
  5. Validates that all supplier groups and price list exist before creating

Update Supplier

PUT /suppliers/:id Update an existing supplier. You may update core fields, default price list, address, and contact. If address/contact do not exist, they will be created. Requires Authentication: Bearer token in Authorization header

Required Headers

KeyValueRequiredDescription
x-store-idstringYesStore identifier (used by @StoreId() decorator)

Path Parameters

ParameterTypeRequiredDescription
idstringYesThe ID of the supplier

Request Body

All fields are optional (partial updates supported):
FieldTypeRequiredDescription
namestringNoSupplier name (max 255 characters)
descriptionstringNoDescription (max 1000 characters)
notestringNoInternal note (max 1000 characters)
defaultPriceListIdstringNoDefault price list to assign
isActivebooleanNoActive status
addressAddressInputNoAddress to create/update
contactContactInputNoContact to create/update

Request Body Example

{
  "name": "Global Traders Ltd. (Updated)",
  "description": "Updated description",
  "note": "Priority handling",
  "defaultPriceListId": "pl_789xyz",
  "isActive": true,
  "address": {
    "street": "456 New Ave",
    "city": "Rabat",
    "state": "RA",
    "postalCode": "10000",
    "country": "MA"
  },
  "contact": {
    "phone": "+212600999888",
    "fax": null,
    "email": "[email protected]",
    "website": "https://globaltraders.com/contact"
  }
}

Success Response

Status Code: 200 OK Returns the updated Supplier object (same shape as in “Get Supplier by ID”).

Possible Errors

CodeMessage
400Bad Request - Validation errors
403Forbidden - User does not have access to this store or supplier
404Not Found - Supplier or price list not found
500Internal Server Error - Failed to update supplier

Example Requests

curl -X PUT "https://joptic.jethings.com/suppliers/sup_abc123" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Updated Supplier Name",
    "description": "Updated description",
    "isActive": true
  }'
When updating a supplier:
  • Only provided fields are updated (partial updates supported)
  • Address fields are updated in the linked address record, or a new address record is created if one doesn’t exist
  • Contact fields are updated in the linked contact record, or a new contact record is created if one doesn’t exist
  • The updatedAt timestamp is automatically updated
  • Price list is validated if provided

Delete Single Supplier

DELETE /suppliers/:id Delete a single supplier by its ID. This is a permanent deletion. Requires Authentication: Bearer token in Authorization header Rate Limited: 5 requests per minute

Path Parameters

ParameterTypeRequiredDescription
idstringYesThe ID of the supplier

Success Response

Status Code: 200 OK
{
  "message": "Supplier deleted successfully"
}

Possible Errors

CodeMessage
404Not Found - Supplier not found
429Too Many Requests - Rate limit exceeded
500Internal Server Error

Example Requests

curl -X DELETE "https://joptic.jethings.com/suppliers/sup_12345" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"
This operation permanently deletes the supplier. Make sure to verify the supplier ID before deletion. Consider checking for dependent records (like purchase orders or invoices) before deleting.

Bulk Delete Suppliers

DELETE /suppliers Delete multiple suppliers by their IDs. This is a permanent deletion. Requires Authentication: Bearer token in Authorization header Rate Limited: 3 requests per minute

Request Body

FieldTypeRequiredDescription
idsstring[] UUID✅ YesSupplier IDs to delete

Request Body Example

{
  "ids": ["sup_123", "sup_456", "sup_789"]
}

Success Response

Status Code: 200 OK
{
  "message": "Successfully deleted 3 supplier(s)",
  "deletedCount": 3
}

Possible Errors

CodeMessage
400Bad Request - No supplier IDs provided
404Not Found - No valid suppliers found to delete
429Too Many Requests - Rate limit exceeded
500Internal Server Error

Example Requests

curl -X DELETE "https://joptic.jethings.com/suppliers" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["sup_123", "sup_456", "sup_789"]
  }'
This operation permanently deletes multiple suppliers. Only valid supplier IDs are deleted (invalid IDs are ignored). Returns the count of successfully deleted suppliers. Rate limited to 3 requests per minute for safety.

Business Logic

Supplier Creation Flow

Store LinkingWhen creating a supplier:
  1. The supplier is automatically linked to the authenticated user’s store
  2. Store access is validated before creation
  3. The supplier can be linked to multiple stores via storeIds array
  4. Store linking is managed through supplierStoreRelation table
Supplier Group AssignmentSuppliers can be assigned to groups:
  1. Provide supplierGroupIds array in the request
  2. All supplier group IDs are validated before creation
  3. If any group doesn’t exist, creation fails with 404
  4. Groups are linked through supplierGroupRelation table
  5. A supplier can belong to multiple groups
Address ManagementAddress information is optional:
  1. If address is provided, a new address record is created
  2. All address fields are required if address object is provided
  3. The address is automatically linked to the supplier via addressId
  4. Address can be updated independently when updating the supplier
  5. If address doesn’t exist and is provided in update, a new address is created
Contact ManagementContact information is optional:
  1. If contact is provided, a new contact record is created
  2. Contact fields (phone, fax, email, website) are all optional
  3. The contact is automatically linked to the supplier via contactId
  4. Contact can be updated independently when updating the supplier
  5. If contact doesn’t exist and is provided in update, a new contact is created
Default Price ListSuppliers can have a default price list:
  1. Provide defaultPriceListId in the request
  2. Price list is validated before creation/update
  3. If price list doesn’t exist, operation fails with 404
  4. Default price list can be changed or removed (set to null) on update
Store Access ValidationAccess control is enforced:
  1. User must have an active relation with the store to access its suppliers
  2. Store access is validated on every request
  3. Users can only create/update/delete suppliers for stores they have access to
  4. Supplier operations are scoped to the authenticated user’s accessible stores
Partial UpdatesThe update endpoint supports flexible partial updates:
  1. Only provided fields are updated
  2. Address fields can be updated independently
  3. Contact fields can be updated independently
  4. Supplier fields (name, description, note, defaultPriceListId, isActive) can be updated independently
  5. This allows fine-grained control over what gets updated

Supplier States

Active Supplier

Active (isActive: true)
Supplier is currently in use and available for operations

Grouped Supplier

Grouped (has supplierGroups)
Supplier belongs to one or more supplier groups

With Address

Address Information
Supplier has associated address details

With Contact

Contact Information
Supplier has associated contact details (phone, email, website)

Error Responses

All endpoints may return standard HTTP error codes:
  • 400 Bad Request - Invalid request parameters or body
  • 403 Forbidden - User does not have access to the store or supplier
  • 404 Not Found - Resource not found (supplier, store, price list, supplier group, etc.)
  • 429 Too Many Requests - Rate limit exceeded
  • 500 Internal Server Error - Server error

Error Response Format

{
  "statusCode": 403,
  "message": "You do not have access to this store",
  "error": "Forbidden"
}

Common Error Scenarios

403 Forbidden - Store Access
{
  "statusCode": 403,
  "message": "You do not have access to this store"
}
Occurs when the user doesn’t have an active relation with the specified store. 403 Forbidden - Supplier Access
{
  "statusCode": 403,
  "message": "You do not have access to this supplier"
}
Occurs when the supplier is not linked to the user’s accessible store. 404 Not Found - Supplier
{
  "statusCode": 404,
  "message": "Supplier not found"
}
Occurs when the supplier ID doesn’t exist. 404 Not Found - Price List or Group
{
  "statusCode": 404,
  "message": "Price list not found"
}
Occurs when a referenced price list or supplier group doesn’t exist. 400 Bad Request - Validation Error
{
  "statusCode": 400,
  "message": ["name must be a string", "address.street must be a string"],
  "error": "Bad Request"
}
Occurs when required fields are missing or data types are incorrect.

Usage Examples

Example 1: Create a supplier with all information

curl -X POST "https://joptic.jethings.com/suppliers" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Lens Supplier Inc",
    "description": "Premium lens supplier",
    "supplierGroupIds": ["grp_1"],
    "defaultPriceListId": "pl_123",
    "address": {
      "street": "123 Optical Street",
      "city": "New York",
      "state": "NY",
      "postalCode": "10001",
      "country": "United States"
    },
    "contact": {
      "phone": "+1-555-123-4567",
      "email": "[email protected]"
    }
  }'

Example 2: Create a supplier with minimal information

curl -X POST "https://joptic.jethings.com/suppliers" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Basic Supplier"
  }'

Example 3: Update supplier contact only

curl -X PUT "https://joptic.jethings.com/suppliers/sup_123" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "contact": {
      "phone": "+1-555-999-8888",
      "email": "[email protected]"
    }
  }'

Example 4: Bulk delete suppliers

curl -X DELETE "https://joptic.jethings.com/suppliers" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["sup_123", "sup_456"]
  }'

Best Practices

  • Use supplier groups to organize suppliers by category or type
  • Assign default price lists for easier price management
  • Keep supplier names descriptive and consistent
  • Use notes field for important supplier-specific information
  • Provide complete address information for accurate location tracking
  • Keep contact information up to date for easy communication
  • Use consistent address formats across suppliers
  • Update contact information when personnel changes
  • Only send fields that need to be updated
  • Update address and contact information independently when possible
  • Use partial updates to avoid overwriting unchanged data
  • Verify supplier ID before updating
  • Verify supplier ID before deletion
  • Check for dependent records (purchase orders, invoices) before deleting
  • Use bulk delete for multiple suppliers (rate limited to 3 per minute)
  • Single delete is rate limited to 5 per minute
  • Deletion is permanent and cannot be undone
  • DELETE endpoints are rate limited for safety
  • Single delete: 5 requests per minute
  • Bulk delete: 3 requests per minute
  • Implement retry logic with exponential backoff for rate limit errors

Suppliers work in conjunction with other inventory management endpoints:
  • Supplier Groups API (/supplier-groups) - Manage supplier groups and assign suppliers
  • Price List API (/price-lists) - Manage price lists that can be assigned to suppliers
  • Purchase Invoice API (/purchase-invoices) - Create purchase invoices from suppliers
  • Purchase Order API (/purchase-orders) - Create purchase orders with suppliers
Suppliers are essential for managing vendor relationships in optical stores. They can be organized into groups, assigned default price lists, and linked to multiple stores. Use suppliers to track vendor information and manage purchasing relationships.

Summary

EndpointMethodDescription
/suppliersGETRetrieve paginated list of suppliers
/suppliers/:idGETRetrieve a single supplier by ID
/suppliersPOSTCreate a new supplier
/suppliers/:idPUTUpdate an existing supplier (partial updates)
/suppliers/:idDELETEDelete a single supplier (rate limited: 5/min)
/suppliersDELETEBulk delete suppliers (rate limited: 3/min)