Introduction
Welcome to our documentation. Here you'll find everything you need to get started.
Top-level 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:
- Log in to your Circular AI account at https://getcircular.ai/${organizationSlug}/
- Navigate to the API section in your account settings
- Click on "Generate New API Key"
- Name your key and select the appropriate access level
- 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:
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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.
Search
POST api/organization_slug/search
Will search all entity types across your whole organization in natural language.
const replies = await fetch(https://getcircular.ai/api/${organizationSlug}/search, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify({
query: 'People selling red dresses for the summer',
})
Response
Will return a list of replies.
[
{
"entity_type": "seller",
"entity_id": "123e4567-e89b-12d3-a456-426614174000",
},
{
"entity_type": "seller",
"entity_id": "a596d46e-2f14-4996-abd9-e8913f0c1728",
},
{
"entity_type": "product",
"entity_id": "afbaee20-bc40-49e4-a6ba-920738ed5f0f",
]
... more results
}
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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
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.
{
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.
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.
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.