Skip to main content

Customer API

The Customer API allows you to create optical customers with their personal information, prescription data, and manage customer records. This endpoint supports file uploads for prescriptions and other documents.
This endpoint uses multipart/form-data format to support file uploads. Make sure to include the proper headers when making requests.

Endpoint Details

Base URL: https://joptic.jethings.com

Required Headers

x-store-id: <store_id>
Authorization: Bearer <token>

Create Customer

Endpoint

Endpoint: POST /customer
Content-Type: multipart/form-data

Request Structure

The request uses multipart/form-data format with:
  • jsonData: A JSON string containing the customer data
  • Files: Optional prescription documents or attachments uploaded with specific field names

Customer Data Structure

The jsonData field must contain a JSON string with the following structure:
FieldTypeRequiredDescription
customerDataobject✅ YesPersonal information of the customer
addressobjectNoCustomer address details
opticDataobjectNoOptical prescription measurements

CustomerData Object

FieldTypeRequiredDescription
firstNamestring✅ YesCustomer’s first name
lastNamestring✅ YesCustomer’s last name
emailstring✅ YesCustomer’s email address (unique)
phoneNumberstring✅ YesCustomer’s phone number
dateOfBirthstring (ISO Date)✅ YesDate of birth in ISO format
sexstring✅ YesGender: “male” or “female”

Address Object

FieldTypeRequiredDescription
streetstringNoStreet address
citystringNoCity name
zipCodestringNoPostal/ZIP code
statestringNoState or province

OpticData Object

Contains vision prescription data for different viewing distances:
FieldTypeRequiredDescription
distanceVisionobjectNoDistance vision prescription
intermediateVisionobjectNoIntermediate vision prescription
nearVisionobjectNoNear vision prescription
Each vision object contains rightEye and leftEye with:
  • sph (sphere): Spherical power
  • cyl (cylinder): Cylindrical power
  • axis: Axis measurement

Create Customer Examples

Example 1: Create Customer with Full Optical Data

curl -X POST https://joptic.jethings.com/customer \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -F 'jsonData={
    "customerData": {
      "firstName": "Layla",
      "lastName": "Hassan",
      "email": "[email protected]",
      "phoneNumber": "+1-555-0199",
      "dateOfBirth": "1992-05-12T00:00:00.000Z",
      "sex": "female"
    },
    "address": {
      "street": "123 Vision Ave",
      "city": "Casablanca",
      "zipCode": "20000",
      "state": "MA"
    },
    "opticData": {
      "distanceVision": {
        "rightEye": { "sph": "-1.25", "cyl": "-0.25", "axis": "90" },
        "leftEye": { "sph": "-1.00", "cyl": "-0.25", "axis": "85" }
      },
      "intermediateVision": {
        "rightEye": { "sph": "-0.75", "cyl": "0", "axis": "0" },
        "leftEye": { "sph": "-0.75", "cyl": "0", "axis": "0" }
      },
      "nearVision": {
        "rightEye": { "sph": "-0.50", "cyl": "0", "axis": "0" },
        "leftEye": { "sph": "-0.50", "cyl": "0", "axis": "0" }
      }
    }
  }' \
  -F '[email protected]' \
  -F '[email protected]'
Success Response:
{
  "customer": {
    "id": "customer-uuid",
    "isActive": true,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T10:00:00.000Z",
    "deletedAt": null
  },
  "localData": {
    "id": "local-data-uuid",
    "firstName": "Layla",
    "lastName": "Hassan",
    "email": "[email protected]",
    "phoneNumber": "+1-555-0199",
    "dateOfBirth": "1992-05-12T00:00:00.000Z",
    "sex": "female",
    "customerId": "customer-uuid"
  }
}

Example 2: Create Customer with Minimal Data

curl -X POST https://joptic.jethings.com/customer \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -F 'jsonData={
    "customerData": {
      "firstName": "Ahmed",
      "lastName": "Benali",
      "email": "[email protected]",
      "phoneNumber": "+212-6-12345678",
      "dateOfBirth": "1985-08-20T00:00:00.000Z",
      "sex": "male"
    }
  }'

Get Single Customer

Retrieve a specific customer by ID with all their details including personal information, address, and optical prescription data.

Endpoint

Endpoint: GET /customer/:id

Path Parameters

ParameterTypeRequiredDescription
idstring✅ YesThe ID of the customer

Response (Success)

{
  "id": "customer-uuid",
  "isActive": true,
  "createdAt": "2024-01-15T10:00:00.000Z",
  "updatedAt": "2024-01-15T10:00:00.000Z",
  "deletedAt": null,
  "localData": {
    "id": "local-data-uuid",
    "firstName": "Layla",
    "lastName": "Hassan",
    "email": "[email protected]",
    "phoneNumber": "+1-555-0199",
    "dateOfBirth": "1992-05-12T00:00:00.000Z",
    "sex": "female",
    "customerId": "customer-uuid"
  },
  "address": {
    "id": "address-uuid",
    "street": "123 Vision Ave",
    "city": "Casablanca",
    "zipCode": "20000",
    "state": "MA",
    "customerId": "customer-uuid"
  },
  "opticData": {
    "id": "optic-data-uuid",
    "distanceVision": {
      "rightEye": { "sph": "-1.25", "cyl": "-0.25", "axis": "90" },
      "leftEye": { "sph": "-1.00", "cyl": "-0.25", "axis": "85" }
    },
    "intermediateVision": {
      "rightEye": { "sph": "-0.75", "cyl": "0", "axis": "0" },
      "leftEye": { "sph": "-0.75", "cyl": "0", "axis": "0" }
    },
    "nearVision": {
      "rightEye": { "sph": "-0.50", "cyl": "0", "axis": "0" },
      "leftEye": { "sph": "-0.50", "cyl": "0", "axis": "0" }
    },
    "customerId": "customer-uuid"
  }
}

Response Schema

  • id (string): The customer ID
  • isActive (boolean): Whether the customer is active
  • createdAt (Date): Creation timestamp
  • updatedAt (Date): Last update timestamp
  • deletedAt (Date, nullable): Deletion timestamp (null if not deleted)
  • localData (object): Customer personal information
  • address (object, optional): Customer address details
  • opticData (object, optional): Optical prescription data

Error Responses

404 Not Found - Customer not found or doesn’t belong to store
{
  "statusCode": 404,
  "message": "Customer not found or does not belong to this store"
}

Example Request

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

Get All Customers

Endpoint

Endpoint: GET /customer

Query Parameters

ParameterTypeRequiredDefaultDescription
searchstringNo-Search by first name, last name, or username
namestringNo-Filter by specific name (first or last)
isActivebooleanNo-Filter by active status
pagenumberNo1Page number for pagination
limitnumberNo10Number of records per page
sortBystringNocreatedAtSort field: “name”, “status”, “createdAt”, “updatedAt”
sortOrderstringNodescSort order: “asc” or “desc”

Get Customers Examples

Example 1: Get All Customers (Default Pagination)

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

Example 2: Search Customers with Filters

curl -X GET "https://joptic.jethings.com/customer?search=Hassan&isActive=true&page=1&limit=20&sortBy=name&sortOrder=asc" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"
Success Response:
{
  "data": [
    {
      "id": "customer-uuid",
      "name": "Layla Hassan",
      "userName": "[email protected]",
      "groups": [
        {
          "id": "group-uuid",
          "name": "VIP Customers"
        }
      ],
      "isLocal": true,
      "status": "active",
      "createdAt": "2024-01-15T10:00:00.000Z",
      "updatedAt": "2024-01-15T10:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 45,
    "totalPages": 3
  }
}

Example 3: Get Active Customers Only

curl -X GET "https://joptic.jethings.com/customer?isActive=true&sortBy=createdAt&sortOrder=desc" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"

Update Customer

Update an existing customer’s information. You can update personal data, address, and optical prescription information. This endpoint also supports file uploads for updating prescription documents.

Endpoint

Endpoint: PUT /customer/:id
Content-Type: multipart/form-data

Path Parameters

ParameterTypeRequiredDescription
idstring✅ YesThe ID of the customer

Request Structure

The request uses multipart/form-data format with:
  • jsonData: A JSON string containing the customer data to update
  • Files: Optional prescription documents or attachments to update

Request Body Schema

All fields in the jsonData are optional. Only include the fields you want to update:
FieldTypeRequiredDescription
customerDataobjectNoPersonal information of the customer
addressobjectNoCustomer address details
opticDataobjectNoOptical prescription measurements

CustomerData Object (all fields optional)

FieldTypeDescription
firstNamestringCustomer’s first name
lastNamestringCustomer’s last name
emailstringCustomer’s email address (unique)
phoneNumberstringCustomer’s phone number
dateOfBirthstring (ISO Date)Date of birth in ISO format
sexstringGender: “male” or “female”

Request Body Examples

Update only customer name:
{
  "customerData": {
    "firstName": "Layla",
    "lastName": "Hassan"
  }
}
Update multiple fields:
{
  "customerData": {
    "firstName": "Layla",
    "lastName": "Hassan",
    "phoneNumber": "+1-555-9999"
  },
  "address": {
    "street": "456 New Street",
    "city": "Rabat",
    "zipCode": "10000",
    "state": "RB"
  },
  "opticData": {
    "distanceVision": {
      "rightEye": { "sph": "-1.50", "cyl": "-0.50", "axis": "90" },
      "leftEye": { "sph": "-1.25", "cyl": "-0.50", "axis": "85" }
    }
  }
}

Response (Success)

{
  "customer": {
    "id": "customer-uuid",
    "isActive": true,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T11:45:00.000Z",
    "deletedAt": null
  },
  "localData": {
    "id": "local-data-uuid",
    "firstName": "Layla",
    "lastName": "Hassan",
    "email": "[email protected]",
    "phoneNumber": "+1-555-9999",
    "dateOfBirth": "1992-05-12T00:00:00.000Z",
    "sex": "female",
    "customerId": "customer-uuid"
  }
}

Error Responses

404 Not Found - Customer not found or doesn’t belong to store
{
  "statusCode": 404,
  "message": "Customer not found or does not belong to this store"
}
400 Bad Request - Validation errors
{
  "statusCode": 400,
  "message": [
    "firstName must be a string",
    "email must be a valid email address"
  ],
  "error": "Bad Request"
}
409 Conflict - Email already exists
{
  "statusCode": 409,
  "message": "Customer with this email already exists",
  "error": "Conflict"
}

Example Requests

curl -X PUT "https://joptic.jethings.com/customer/customer-uuid" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>" \
  -F 'jsonData={
    "customerData": {
      "firstName": "Layla",
      "lastName": "Hassan",
      "phoneNumber": "+1-555-9999"
    }
  }'

Notes

  • Only customers belonging to the authenticated user’s store can be updated
  • Email addresses must be unique per store
  • All updates are performed within a database transaction
  • Files can be updated by including new files with the same field names
  • If a field is not included in the request, it will remain unchanged

Delete Customer

Delete a single customer. This performs a soft delete, setting the deletedAt timestamp.

Endpoint

Endpoint: DELETE /customer/:id

Path Parameters

ParameterTypeRequiredDescription
idstring✅ YesThe ID of the customer

Response (Success)

{
  "message": "Customer successfully deleted",
  "customerId": "customer-uuid"
}

Response Schema

  • message (string): Success message
  • customerId (string): The ID of the deleted customer

Error Responses

404 Not Found - Customer not found or doesn’t belong to store
{
  "statusCode": 404,
  "message": "Customer not found or does not belong to this store"
}

Example Request

curl -X DELETE "https://joptic.jethings.com/customer/customer-uuid" \
  -H "x-store-id: your-store-id" \
  -H "Authorization: Bearer <token>"

Notes

  • Only customers belonging to the authenticated user’s store can be deleted
  • This is a soft delete operation (sets deletedAt timestamp)
  • Deleted customers are automatically excluded from GET requests
  • Related data (address, opticData) may be handled based on cascade rules

Bulk Delete Customers

Delete multiple customers in a single request. Only customers belonging to the authenticated user’s store can be deleted.

Endpoint

Endpoint: DELETE /customer/bulk

Request Body

{
  "ids": [
    "customer-uuid-1",
    "customer-uuid-2",
    "customer-uuid-3"
  ]
}

Request Body Schema

  • ids (array of strings, required, min 1 item): Array of customer IDs to delete
    • Each ID must be a non-empty string

Response (Success)

{
  "message": "Successfully deleted 3 customer(s)",
  "deletedCount": 3
}

Response Schema

  • message (string): Success message indicating how many customers were deleted
  • deletedCount (number): Number of customers successfully deleted

Error Responses

400 Bad Request - No customer IDs provided
{
  "statusCode": 400,
  "message": "No customer IDs provided"
}
404 Not Found - No valid customers found to delete
{
  "statusCode": 404,
  "message": "No valid customers found to delete"
}

Example Request

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

Notes

  • Only customers that belong to the authenticated user’s store will be deleted
  • Already deleted customers (soft-deleted) are automatically excluded
  • This is a soft delete operation (sets deletedAt timestamp)
  • All operations are performed within a database transaction

Response Structures

Create Customer Response

{
  "customer": {
    "id": "customer-uuid",
    "isActive": true,
    "createdAt": "2024-01-15T10:00:00.000Z",
    "updatedAt": "2024-01-15T10:00:00.000Z",
    "deletedAt": null
  },
  "localData": {
    "id": "local-data-uuid",
    "firstName": "string",
    "lastName": "string",
    "email": "string",
    "phoneNumber": "string",
    "dateOfBirth": "ISO date string",
    "sex": "male | female",
    "customerId": "customer-uuid"
  }
}

Get Customers Response

{
  "data": [
    {
      "id": "customer-uuid",
      "name": "Full Name",
      "userName": "[email protected]",
      "groups": [
        {
          "id": "group-uuid",
          "name": "Group Name"
        }
      ],
      "isLocal": true,
      "status": "active | unactive",
      "createdAt": "ISO date string",
      "updatedAt": "ISO date string"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10,
    "total": 100,
    "totalPages": 10
  }
}

Error Responses

400 Bad Request

{
  "statusCode": 400,
  "message": "Invalid input data or missing required fields",
  "error": "Bad Request"
}

403 Forbidden

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

409 Conflict

{
  "statusCode": 409,
  "message": "Customer with this email already exists",
  "error": "Conflict"
}

500 Internal Server Error

{
  "statusCode": 500,
  "message": "Failed to create customer",
  "error": "Internal Server Error"
}

Important Notes

Store Access Requirements

  • Requires user to be an owner of the store
  • Validates userStoreRelation for active owner relationship
  • Missing ownership raises ForbiddenException
  • Requires user to have access to the store
  • Can be owner or have assigned role/permissions
  • Store access is validated via userStoreRelation
  • Requires user to be an owner of the store
  • Only customers belonging to the store can be updated
  • Email uniqueness is validated per store
  • Requires user to be an owner of the store
  • Only customers belonging to the store can be deleted
  • Performs soft delete (sets deletedAt timestamp)

Email Handling

  • Email addresses are trimmed and stored as provided
  • Email must be unique per store
  • Duplicate emails result in ConflictException (409)

File Uploads

Supported Files

Upload prescription documents, ID cards, or related files
Grouped by fieldname

Optional Upload

Files are optional for customer creation
Basic customer data sufficient

Customer Status

  • Customers are created with isActive: true by default
  • Status can be filtered in GET requests using isActive parameter
  • Deleted customers have deletedAt timestamp (soft delete)

Name Display Logic

The customer name follows this fallback order:
  1. firstName + lastName from local data
  2. userName from user table
  3. Placeholder value if neither available

Pagination

{
  page: 1,
  limit: 10,
  sortBy: "createdAt",
  sortOrder: "desc"
}
Returns 10 most recent customers by default

Summary

1

Authenticate

Obtain JWT token and ensure store access
2

Prepare Customer Data

Structure JSON with required customer information
3

Create Customer

POST request with multipart form data
4

Retrieve Customers

GET request with optional filters and pagination, or GET single customer by ID
5

Update Customer

PUT request with customer ID and updated data (multipart/form-data)
6

Delete Customer

DELETE request with customer ID, or bulk delete with array of IDs
7

Handle Response

Process customer data, pagination metadata, or deletion confirmation
This API provides comprehensive customer management for optical stores, including prescription data storage, file uploads, flexible querying with pagination and filtering, and full CRUD operations for customer records.