Introduction

Welcome to our documentation. Here you'll find everything you need to get started.

Top-level architecture

Architecture

Event- and API-driven architecture. When something happens in Circular or the third party (POS, ECOM, ERP, ETC) we react on that event and sync the data immediately, preferably over REST APIs. All functionality offered through our SaaS offering, is also available in the API, so you can build our functionality into your stack with ease. We recommend anyone using the white-label solutions first and building custom front-end later.

APIs

Our standard set of APIs function exactly the same, with a single exception: the /organizations endpoint does not offer a DELETE method.

API Authentication

Circular AI uses API key authentication for secure access to our APIs.

1. Obtaining an API Key

To get an API key, follow these steps:

  1. Log in to your Circular AI account at https://getcircular.ai/${organizationSlug}/
  2. Navigate to the API section in your account settings
  3. Click on "Generate New API Key"
  4. Name your key and select the appropriate access level
  5. Copy and securely store your API key. Note: It will only be shown once for security reasons.

2. Using the API Key

Include your API key in the Authorization header of all API requests:

api-request.ts
const apiUrl = 'https://getcircular.ai/api/${organizationSlug}/some-endpoint'
const apiKey = 'YOUR_API_KEY'

const response = await fetch(apiUrl, {
  method: 'GET', // or POST, PATCH, etc.
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    'Content-Type': 'application/json'
  }
})

const data = await response.json()

API Key Best Practices

  • Keep your API key secure and never share it publicly
  • Use environment variables to store your API key in your application
  • Rotate your API keys periodically for enhanced security
  • Use different API keys for development and production environments

API Access Levels

Circular AI offers different access levels for API keys:

  • Read-Only - Allows only GET requests to fetch data
  • Read-Write - Allows GET, POST, PATCH, and DELETE requests
  • Admin - Full access to all API endpoints, including administrative functions

Support

If you encounter any issues with API authentication or have questions, please contact our support team at hei@getcircular.ai.

Brands

GET api/organization_slug/brands

Will fetch all brands for your organization, will also include the brands that you have created yourself.

Parameters

  • since - Only fetch brands created after this date and time, in epoch.
    Optional
  • take - number of brands to fetch.
    Optional
  • skip - number of brands to skip.
    Optional

Request

fetch-brands.ts
const brands = await fetch(https://getcircular.ai/api/${organizationSlug}/brands, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of brands including the pagination data.

fetch-brands-response.ts
{
  data: Brand[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/brands

Will create a brand for your organization. You cant set the id or brand_slug when creating a brand.

You can either pass it a single Brand object, or an array of Brand objects.

create-brand.ts
const newBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/brands, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Helly Hansen',  // only the required field
})

PATCH api/organization_slug/brands

Will update a brand for your organization, it requires the id or brand_slug to be present. You cant update the id when updating a brand.

You can either pass it a single Brand object, or an array of Brand objects.

update-brand.ts
const updatedBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/brands, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
  id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the brand to update
  brand_slug: 'helly-hansen',                   // The slug of the brand to update
  name: 'Helly Hansen',
})

Categories

GET api/organization_slug/categories

Will fetch all categories for your organization, will also include the categories that you have created yourself.

Parameters

  • since - Only fetch categories created after this date and time, in epoch.
    Optional
  • take - number of categories to fetch.
    Optional
  • skip - number of categories to skip.
    Optional
fetch-categories.ts
const categories = await fetch(https://getcircular.ai/api/${organizationSlug}/categories, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  }

Response

Will return a list of categories.

fetch-categories-response.ts
{
  data: Category[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/categories

Will create a category for your organization. You cant set the id or category_slug when creating a category.

You can either pass it a single Category object, or an array of Category objects.

This should not be used to translate category names to a different language, that will make the AI pricing assistant very confused.

create-category.ts
const newCategory = await fetch(https://getcircular.ai/api/${organizationSlug}/categories, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
  name: 'Pants',  // only the required field
})

PATCH api/organization_slug/categories

Will update a category for your organization, it requires the id or category_slug to be present. You cant update the id when updating a category.

You can either pass it a single Category object, or an array of Category objects.

update-category.ts
const updatedCategory = await fetch(https://getcircular.ai/api/${organizationSlug}/categories, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Pants',
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the category to update
    category_slug: 'pants',                   // The slug of the category to update
})

Colors

Fetch all colors for your organization, will also include the colors that you have created yourself.

GET api/organization_slug/colors

Will fetch all colors for your organization, will also include the colors that you have created yourself.

Parameters

  • since - Only fetch colors created after this date and time, in epoch.
    Optional
  • take - number of colors to fetch.
    Optional
  • skip - number of colors to skip.
    Optional
fetch-colors.ts
const colors = await fetch(https://getcircular.ai/api/${organizationSlug}/colors, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  }

Response

Will return a list of colors.

fetch-colors-response.ts
{
  data: Color[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/colors

Will create a color for your organization. You cant set the id or color_slug when creating a color.

You can either pass it a single Color object, or an array of Color objects.

create-color.ts
const newColor = await fetch(https://getcircular.ai/api/${organizationSlug}/colors, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Turquoise',
})

PATCH api/organization_slug/colors

Will update a color for your organization, it requires the id or color_slug to be present. You cant update the id when updating a color.

You can either pass it a single Color object, or an array of Color objects.

This should not be used to translate color names to a different language, that will make the AI pricing assistant very confused.

update-color.ts
const updatedColor = await fetch(https://getcircular.ai/api/${organizationSlug}/colors, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Turquoise',
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the color to update
    color_slug: 'turquoise',                   // The slug of the color to update
})

Coworkers

GET api/organization_slug/coworkers

Will fetch all coworkers for your organization, will also include the coworkers that you have created yourself.

Parameters

  • since - Only fetch coworkers created after this date and time, in epoch.
    Optional
  • take - number of coworkers to fetch.
    Optional
  • skip - number of coworkers to skip.
    Optional
fetch-coworkers.ts
const coworkers = await fetch(https://getcircular.ai/api/${organizationSlug}/coworkers, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of coworkers.

fetch-coworkers-response.ts
{
  data: Coworker[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/coworkers

Will create a coworker for your organization. You cant set the id when creating a coworker.

You can either pass it a single Coworker object, or an array of Coworker objects.

create-coworker.ts
const newCoworker = await fetch(https://getcircular.ai/api/${organizationSlug}/coworkers, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    first_name: 'Bjørn',
    last_name: 'Bakke',
    email: 'bjorn@getcircular.ai',
    phone: '+4790000000',
    default_store_id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',
})

PATCH api/organization_slug/coworkers

Will update a coworker for your organization, it requires the id to be present. You cant update the id when updating a coworker.

You can either pass it a single Coworker object, or an array of Coworker objects.

update-coworker.ts
const updatedCoworker = await fetch(https://getcircular.ai/api/${organizationSlug}/coworkers, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the coworker to update
    first_name: 'Bjørn',
    last_name: 'Bakke',
    email: 'bjorn@getcircular.ai',
    phone: '+4790000000',
    default_store_id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',
})

Deliveries

GET api/organization_slug/deliveries

Will fetch all deliveries for your organization, will also include the deliveries that you have created yourself.

Parameters

  • since - Only fetch deliveries created after this date and time, in epoch.
    Optional
  • take - number of deliveries to fetch.
    Optional
  • skip - number of deliveries to skip.
    Optional
fetch-deliveries.ts
const deliveries = await fetch(https://getcircular.ai/api/${organizationSlug}/deliveries, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of deliveries.

fetch-brands-response.ts
{
  data: Delivery[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/deliveries

Will create a delivery for your organization. You cant set the id when creating a delivery.

You can either pass it a single Delivery object, or an array of Delivery objects.

create-delivery.ts
const newBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/deliveries, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
  seller_id: '0d43f15d-d319-48ae-bb28-a79e15cd021a',
  store_id: '4bf70697-8c0b-4c21-8602-b3f06c881cb5',
  status: 'completed',
  delivery_at: '2024-11-12T23:32:17.554Z',
})

PATCH api/organization_slug/deliveries

Will update a delivery for your organization, it requires the id to be present. You cant update the id when updating a delivery.

You can either pass it a single Delivery object, or an array of Delivery objects.

Deliveries that are completed more than 24 hours ago cannot be updated.

update-delivery.ts
const updatedBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/deliveries, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
  id: '06c11be9-199a-40f2-bdaa-6e8e32fba761',   // The id of the delivery to update
  seller_id: '0d43f15d-d319-48ae-bb28-a79e15cd021a',
  store_id: '4bf70697-8c0b-4c21-8602-b3f06c881cb5',
  status: 'completed',
  delivery_at: '2024-11-12T23:32:17.554Z',
})

Locations

Locations are associated with a store and should represent a physical location, the stores has to have a minimum of one location, so not all can be removed.

GET api/organization_slug/locations

Will fetch all locations for your organization, will also include the locations that you have created yourself.

Parameters

  • since - Only fetch locations created after this date and time, in epoch.
    Optional
  • take - number of locations to fetch.
    Optional
  • skip - number of locations to skip.
    Optional
fetch-locations.ts
const locations = await fetch(https://getcircular.ai/api/${organizationSlug}/locations, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  }

Response

Will return a list of locations.

fetch-locations-response.ts
{
  data: Location[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/locations

Will create a location for your organization. You cant set the id when creating a location.

You can either pass it a single Location object, or an array of Location objects.

create-location.ts
const newLocation = await fetch(https://getcircular.ai/api/${organizationSlug}/locations, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Outside',
    store_id: '4bf70697-8c0b-4c21-8602-b3f06c881cb5'
})

PATCH api/organization_slug/locations

Will update a location for your organization, it requires the id to be present. You cant update the id when updating a location.

You can either pass it a single Location object, or an array of Location objects.

update-location.ts
const updatedLocation = await fetch(https://getcircular.ai/api/${organizationSlug}/locations, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',
    store_id: '4bf70697-8c0b-4c21-8602-b3f06c881cb5',
    name: 'Outside'
})

Materials

GET api/organization_slug/materials

Will fetch all materials for your organization, will also include the materials that you have created yourself.

Parameters

  • since - Only fetch materials created after this date and time, in epoch.
    Optional
  • take - number of materials to fetch.
    Optional
  • skip - number of materials to skip.
    Optional
fetch-materials.ts
const materials = await fetch(https://getcircular.ai/api/${organizationSlug}/materials, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of materials.

fetch-materials-response.ts
{
  data: Material[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/materials

Will create a material for your organization. You cant set the id or material_slug when creating a material.

You can either pass it a single Material object, or an array of Material objects.

create-material.ts
const newMaterial = await fetch(https://getcircular.ai/api/${organizationSlug}/materials, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Synthetic Wool'
})

PATCH api/organization_slug/materials

Will update a material for your organization, it requires the id or material_slug to be present. You cant update the id when updating a material.

You can either pass it a single Material object, or an array of Material objects.

update-material.ts
const updatedMaterial = await fetch(https://getcircular.ai/api/${organizationSlug}/materials, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Synthetic Wool'
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the material to update
    material_slug: 'synthetic-wool',
})

Organizations

Organizations are our customers, they can have multiple stores under them. This is the only standardized endpoint that does not support POST, PAGINATION or DELETE.

GET api/organization_slug/organizations

Will fetch all organizations for your organization, will also include the organizations that you have created yourself.

Parameters

  • since - Only fetch organizations created after this date and time, in epoch.
    Optional
fetch-organizations.ts
const organizations = await fetch(https://getcircular.ai/api/${organizationSlug}/organizations, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  }

Response

Will return a list of organizations.

fetch-organizations-response.ts
{
  data: Organization[],
}

PATCH api/organization_slug/organizations

Will update a organization for your organization, it requires the id or organization_slug to be present. You cant update the id when updating a organization.

update-organization.ts
const updatedOrganization = await fetch(https://getcircular.ai/api/${organizationSlug}/organizations, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Best Thrift Store',
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the organization to update
    organization_slug: 'best-thrift-store',            // The slug of the organization to update
    email: 'hello@getcircular.ai',
    consignment_split: 0.5,
    handling_fee_percentage: 10,
    handling_fee_fixed: 10,
    handling_fee_type: 'percentage',
    default_expiry_days: 365,
    default_return_days: 30,
    default_pick_up_days: 14,
    currency: 'EUR',
    phone_number: '+4790000000',
    address_city: 'Oslo',
    address_country: 'Norway',
    address_zip: '0181',
    address_line_1: 'Osloveien',
    address_line_2: null,
    address_region: 'Oslo',
})

Prices

GET api/organization_slug/price/${productId}

Will fetch the predicted best price for a product.

fetch-prices.ts
const prices = await fetch(https://getcircular.ai/api/${organizationSlug}/price/${productId}, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Products

GET api/organization_slug/products

Will fetch all products for your organization.

Parameters

  • since - Only fetch products created after this date and time, in epoch.
    Optional
  • take - number of products to fetch.
    Optional
  • skip - number of products to skip.
    Optional
fetch-products.ts
const products = await fetch(https://getcircular.ai/api/${organizationSlug}/products, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of products.

fetch-products-response.ts
{
  data: Product[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/products/classify

Will classify your product into category, color, brand, material and size, based on a single or a series of pictures.

This is still in very early beta and is not publicly available yet. If you want to be a beta tester contact us at hei@getcircular.ai

POST api/organization_slug/products

Will create a product for your organization. You cant set the id or product_slug when creating a product.

create-product.ts
const newProduct = await fetch(https://getcircular.ai/api/${organizationSlug}/products, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: 'Navy Northface Coat', 
    description: 'Stylish winter coat with excellent insulation.',
    price: 120.99,
    weight: 1.5,
    category_slug: 'coat',
    brand_slug: 'northface',
    size_slug: 'XL',
    color_slug: 'navy',
    material_slug: 'wool',
    attractiveness: 8,
    status: 'active',
    expiring_at: '2024-12-31T23:59:59Z',
    pick_up_at: '2025-01-15T14:00:00Z',
    store_id: 'a2789b01-f2aa-42e0-91e5-9a66149e2f3e',                   // non-nullable
    location_id: '44a5f448-0e2c-459b-af7e-2af239a42263',                // non-nullable
    seller_id: 'ecba6011-90fe-480a-93ac-28851497784b',
  }
})

Additional fields

You can also override the default values for the product, such as consignment_split, handling_fee_fixed, handling_fee_percentage and handling_fee_type, but if they are not set we will use the store default values or the organization default values.

PATCH api/organization_slug/products

Will update a product for your organization, it requires the id or product_slug to be present. You cant update the id or sku when updating a product.

You can either pass it a single Product object, or an array of Product objects.

update-product.ts
const updatedProduct = await fetch(https://getcircular.ai/api/${organizationSlug}/products, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the product to update
    name: 'Navy Northface Coat', 
    description: 'Stylish winter coat with excellent insulation.',
    price: 120.99,
    weight: 1.5,
    category_slug: 'coat',
    brand_slug: 'northface',
    size_slug: 'XL',
    color_slug: 'navy',
    material_slug: 'wool',
    location_id: 'location_56789',
    attractiveness: 8,
    status: 'active',
    expiring_at: '2024-12-31T23:59:59Z',
    pick_up_at: '2025-01-15T14:00:00Z',
    store_id: 'a2789b01-f2aa-42e0-91e5-9a66149e2f3e',                   // non-nullable
    location_id: '44a5f448-0e2c-459b-af7e-2af239a42263',                // non-nullable
    seller_id: 'ecba6011-90fe-480a-93ac-28851497784b',
    modified_by_coworker_id: 'cd3b176f-0c82-4c38-b580-3c24d23e1d4c',
    modified_by_seller_id: 'ecba6011-90fe-480a-93ac-28851497784b'
  }
})

Additional fields

You can also update the default values for the product, such as consignment_split, handling_fee_fixed, handling_fee_percentage and handling_fee_type, but if they are not set we will use the store default values or the organization default values.

Seller remittances

GET api/organization_slug/seller-remittances

Will fetch all seller remittances for your organization.

Parameters

  • since - Only fetch seller remittances created after this date and time, converted to epoch.
    Optional
  • take - number of seller remittances to fetch.
    Optional
  • skip - number of seller remittances to skip.
    Optional
fetch-seller-remittances.ts
const sellerRemittances = await fetch(https://getcircular.ai/api/${organizationSlug}/seller-remittances, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of seller remittances.

fetch-seller-remittances-response.ts
{
data: SellerRemittance[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/seller-remittances

Will create a seller-remittance for your organization. You cant set the id when creating a seller-remittance.

You can either pass it a single SellerRemittance object, or an array of SellerRemittance objects.

create-seller-remittance.ts
const newSellerRemittance = await fetch(https://getcircular.ai/api/${organizationSlug}/seller-remittances, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    seller_id: '44a5f448-0e2c-459b-af7e-2af239a42263',
    requested_at: '2024-01-01T00:00:00Z',
    completed_at: '2024-01-02T00:00:00Z',
    completed_by_coworker_id: 'cd3b176f-0c82-4c38-b580-3c24d23e1d4c',
    approved: true,
    completed: true,
    organization_slug: 'best-thrift-store',
    last_interaction_at: '2024-01-03T00:00:00Z',
    created_by_coworker_id: 'cd3b176f-0c82-4c38-b580-3c24d23e1d4c',
    amount: 100,
})

PATCH api/organization_slug/seller-remittances

Will update a seller-remittance for your organization, it requires the id to be present. You cant update the id when updating a seller-remittance.

You can update the metadata field to update any additional information about the seller-remittance in json format.

You can either pass it a single SellerRemittance object, or an array of SellerRemittance objects.

update-seller-remittance.ts
const updatedBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/seller-remittances, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',               // The id of the seller-remittance to update
    seller_id: '44a5f448-0e2c-459b-af7e-2af239a42263',
    requested_at: '2024-01-01T00:00:00Z',
    completed_at: '2024-01-02T00:00:00Z',
    completed_by_coworker_id: 'cd3b176f-0c82-4c38-b580-3c24d23e1d4c',
    approved: true,
    completed: true,
    organization_slug: 'best-thrift-store',
    last_interaction_at: '2024-01-03T00:00:00Z',
    created_by_coworker_id: 'cd3b176f-0c82-4c38-b580-3c24d23e1d4c',
    amount: 100,
})

Sellers

GET api/organization_slug/sellers

Will fetch all sellers for your organization.

Parameters

  • since - Only fetch sellers created after this date and time, converted to epoch.
    Optional
  • take - number of sellers to fetch.
    Optional
  • skip - number of sellers to skip.
    Optional

Request

fetch-sellers.ts
const sellers = await fetch(https://getcircular.ai/api/${organizationSlug}/sellers, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of all the sellers in your organization, including the pagination data.

fetch-sellers-response.ts
{
  data: Seller[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/sellers

Will create a seller for your organization. You cant set the id when creating a seller. Instead you can use the metadata field to store any additional information about the seller in json format.

You can either pass it a single Seller object, or an array of Seller objects.

create-seller.ts
const newSeller = await fetch(https://getcircular.ai/api/${organizationSlug}/sellers, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    first_name: 'Christoffer',
    last_name: 'Björkman',
    phone_number: '+46701234567',
    email: 'christoffer@getcircular.ai',
    bank_account_number: '1234567890',
    address_country: 'SE',
    address_zip: '12345',
    address_street_1: 'Main Street 1',
    address_street_2: 'Main Street 1',
    address_region: 'Stockholm',
    address_city: 'Stockholm',
    metadata: {
      favorite_color: 'blue',
      // any other custom data you want to add or update
    }
})

PATCH api/organization_slug/sellers

Will create a seller for your organization. You cant set the id when creating a seller. Instead you can use the metadata field to store any additional information about the seller in json format.

You can either pass it a single Seller object, or an array of Seller objects.

update-seller.ts
const updatedSeller = await fetch(https://getcircular.ai/api/${organizationSlug}/sellers, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the seller to update
    first_name: 'Christoffer',
    last_name: 'Björkman',
    phone_number: '+46701234567',
    email: 'christoffer@getcircular.ai',
    bank_account_number: '1234567890',
    address_country: 'SE',
    address_zip: '12345',
    address_street_1: 'Main Street 1',
    address_street_2: 'Main Street 1',
    address_region: 'Stockholm',
    address_city: 'Stockholm',
    metadata: {
      favorite_color: 'blue',
      // any other custom data you want to add or update
    }
})

Sizes

GET api/organization_slug/sizes

Will fetch all sizes for your organization.

Parameters

  • since - Only fetch sizes created after this date and time, converted to epoch.
    Optional
  • take - number of sizes to fetch.
    Optional
  • skip - number of sizes to skip.
    Optional

Request

fetch-sizes.ts
const sizes = await fetch(https://getcircular.ai/api/${organizationSlug}/sizes, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of all the sizes in your organization, including the pagination data.

fetch-sizes-response.ts
{
  data: Size[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/sizes

Will create a size for your organization. You cant set the id or size_slug when creating a size.

You can either pass it a single Size object, or an array of Size objects.

This is not intended to be used to translate existing sizes to your language, but to create new sizes for your organization if you sell things that arent available in the standard sizes we've already created.

The name field is required, and the size_slug will be generated automatically even if you provide one.

create-size.ts
const newBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/sizes, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: '99mm', // only required field
})

PATCH api/organization_slug/sizes

Will update a size for your organization, it requires the id or size_slug to be present to identify which size to update. You cant update the id when updating a size.

You can update the name field, but not the size_slug.

You can either pass it a single Size object, or an array of Size objects.

update-size.ts
const updatedBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/sizes, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    name: '99 millimeters',
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the size to update
    size_slug: '99-mm',                           // The slug of the size to update
  })

Stores

GET api/organization_slug/stores

Will fetch all stores for your organization.

Parameters

  • since - Only fetch stores created after this date and time, converted to epoch.
    Optional
  • take - number of stores to fetch.
    Optional
  • skip - number of stores to skip.
    Optional
fetch-stores.ts
const stores = await fetch(https://getcircular.ai/api/${organizationSlug}/stores, {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
}

Response

Will return a list of stores.

fetch-stores-response.ts
{
  data: Store[],
  pagination: {
    total: number,
    take: number,
    skip: number,
  }
}

POST api/organization_slug/stores

Will create a store for your organization. You cant set the id or store_slug when creating a store.

create-store.ts
const newBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/deliveries, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the store to update
    store_slug: 'hq',                             // The slug of the store to update
    name: 'Headquarters',
    address_city: 'New York',
    address_region: 'NY',
    address_zip: '10001',
    address_country: 'US',
    address_line_1: '123 Main Street',
    address_line_2: 'Suite 100',
    scheduling: {
      monday: {
        open: '09:00',
        close: '17:00',
      },
      tuesday: {
        open: '09:00',
        close: '17:00',
      },
      ...
    },
  })

PATCH api/organization_slug/stores

Will update a store for your organization, it requires the id to be present. You cant update the id when updating a store.

You can either pass it a single Store object, or an array of Store objects.

update-store.ts
const updatedBrand = await fetch(https://getcircular.ai/api/${organizationSlug}/deliveries, {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${accessToken}`
  },
  body: JSON.stringify({
    id: 'ec81f8e4-f3eb-461d-9ab5-00225a7246b8',   // The id of the store to update
    store_slug: 'hq',                             // The slug of the store to update
    name: 'Headquarters',
    address_city: 'New York',
    address_region: 'NY',
    address_zip: '10001',
    address_country: 'US',
    address_line_1: '123 Main Street',
    address_line_2: 'Suite 100',
    scheduling: {
      monday: {
        open: '09:00',
        close: '17:00',
      },
      tuesday: {
        open: '09:00',
        close: '17:00',
      },
      ...
    },
  })

Security

API

Authentication

We use OAuth for secure system-to-system API integrations by issuing tokens that authenticate and authorize each system’s access to specific endpoints. OAuth tokens ensure each system interacts only with approved resources, maintaining data integrity and security across integrations.

Authorization

In the API layer, we enforce organization isolation by automatically filtering requests based on the organization_slug associated with each authenticated user. When a request is made, the API paths must include the organization_slug: api/organization_slug/products, where we ensure only that organizations data is included in the response.

Database level

Auditing

To ensure proper access to sensitive data, we use PostgreSQL auditing to log all key database activities (SELECT, INSERT, UPDATE, DELETE) to track internal access. Each action is associated with an organization_slug, allowing us to audit data access by organization and ensure compliance. Logs focus on sensitive tables and are stored securely for review and accountability.

RLS policies

Every object in the database has a unique organization_slug which is used to identify which organization the data belongs to, we use RLS policies on database level to ensure that only the right organization and their respective users can access the data.

Shared system data, such as currencies and brands, is stored without an organization_slug so that it’s accessible to all tenants. However, each organization can extend these tables with customized entries like brands and colors, which remain isolated and inaccessible to other organizations.