Introduction

The Louvr API provides programmatic access to your organization's data — SKUs, vendors, purchase orders, shipments, and more. All endpoints follow JSON:API conventions and are scoped to the organization tied to your API client.

Base URL

https://ledgeroto.com/api/v1

Content Type

All requests must send and accept JSON.

Content-Type: application/json
Accept: application/json

Authentication

The API uses OAuth 2.0 Client Credentials grants. Each API client is tied to a single organization. You can create and manage API clients from Settings > API Clients in the Louvr dashboard.

Obtaining an Access Token

Exchange your client ID and secret for a Bearer token:

curl -X POST https://ledgeroto.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "scope": "skus:read skus:write vendors:read"
  }'

Response

{
  "token_type": "Bearer",
  "expires_in": 2592000,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbG..."
}

Using the Token

Include the token in the Authorization header of every API request:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbG...
Note: Access tokens expire after 30 days. Client secrets are only shown once at creation time and cannot be retrieved later. If you lose a secret, revoke the client and create a new one.

Pagination

All list endpoints return paginated results. Control page size with the page[size] parameter (default: 25, max: 100).

GET /api/v1/skus?page[size]=50&page[number]=2

Paginated responses include standard Laravel pagination metadata alongside the data array.

Errors

Error responses use standard HTTP status codes with a JSON body:

StatusMeaning
401Unauthenticated — missing or invalid token
403Forbidden — token lacks the required scope, or organization not found
404Resource not found
422Validation error — check the errors object for details
429Rate limited — too many requests
500Server error

422 Validation Error Example

{
  "message": "The sku field is required.",
  "errors": {
    "sku": ["The sku field is required."]
  }
}

Scopes

When requesting a token, specify which scopes your client needs. Each endpoint requires a specific scope.

organizations:read organizations:write skus:read skus:write vendors:read vendors:write locations:read locations:write manufacturers:read manufacturers:write warehouses:read warehouses:write carriers:read carriers:write shipments:read shipments:write

:read scopes allow GET requests. :write scopes allow POST, PUT, and DELETE.

Organizations

Retrieve or update your organization's profile. The API client is permanently bound to one organization — there is no way to switch.

Get Organization

GET /api/v1/organizations/me

Scope: organizations:read

Response 200

{
  "data": {
    "id": "019dc4b7-...",
    "type": "organizations",
    "attributes": {
      "name": "Acme Corp",
      "icon": "building-office",
      "primary_color": "#d97706",
      "secondary_color": "#92400e",
      "created_at": "2026-01-15T10:30:00.000000Z",
      "updated_at": "2026-04-20T14:22:00.000000Z"
    },
    "links": {
      "self": "https://ledgeroto.com/api/v1/organizations/me"
    }
  }
}

Update Organization

PUT /api/v1/organizations/me

Scope: organizations:write

FieldTypeDescription
namestringOrganization name (max 255)
iconstringIcon identifier (max 255)
primary_colorstringHex color, e.g. #d97706 (max 7)
secondary_colorstringHex color (max 7)

SKUs

Manage product SKUs in your catalog. SKUs are the building blocks of purchase orders, inventory tracking, and cost analysis.

List SKUs

GET /api/v1/skus

Scope: skus:read

Create SKU

POST /api/v1/skus

Scope: skus:write — Returns 201 Created

FieldTypeDescription
sku REQUIREDstringSKU identifier (max 255)
name REQUIREDstringProduct name (max 255)
brandstringBrand name
asinstringAmazon ASIN
upcstringUniversal Product Code
fnskustringAmazon FNSKU
gtinstringGlobal Trade Item Number
parent_skustringParent/variation SKU
parent_asinstringParent ASIN for variations
prod_grpstringProduct group
advertised_prod_grpstringAdvertised product group
categorystringProduct category
country_of_originstringCountry of manufacture
hts_codestringHarmonized Tariff Schedule code
materialstringPrimary material
us_duty_pctnumericUS duty percentage (0–100)
canada_duty_pctnumericCanada duty percentage (0–100)
unit_cost_usdnumericUnit cost in USD
map_pricenumericMinimum advertised price
moqintegerMinimum order quantity
is_herobooleanHero product flag
is_hero_for_adsbooleanHero product for advertising
seasonstringSeasonal tag
statusstringProduct status
tagsarrayArray of string tags
weightnumericProduct weight
weight_uomstringWeight unit of measure
units_per_caseintegerUnits per master case
cases_per_palletintegerCases per pallet
unpacked_length_cmnumericUnpacked length (cm)
unpacked_width_cmnumericUnpacked width (cm)
unpacked_height_cmnumericUnpacked height (cm)
unpacked_net_weight_kgnumericUnpacked net weight (kg)
packaged_length_cmnumericPackaged length (cm)
packaged_width_cmnumericPackaged width (cm)
packaged_height_cmnumericPackaged height (cm)
packaged_gross_weight_kgnumericPackaged gross weight (kg)

Get SKU

GET /api/v1/skus/{id}

Scope: skus:read

Update SKU

PUT /api/v1/skus/{id}

Scope: skus:write — Only include fields you want to change.

Delete SKU

DELETE /api/v1/skus/{id}

Scope: skus:write — Returns 204 No Content

Response 200 (Get / List / Update)

{
  "data": {
    "id": "019dc4b7-...",
    "type": "skus",
    "attributes": {
      "sku": "WG-001",
      "name": "Widget Classic",
      "brand": "Acme",
      "asin": "B0EXAMPLE",
      "upc": null,
      "fnsku": "X00EXAMPLE",
      "gtin": null,
      "parent_sku": null,
      "parent_asin": null,
      "prod_grp": "Widgets",
      "advertised_prod_grp": null,
      "category": "Home & Kitchen",
      "country_of_origin": "CN",
      "hts_code": "3924.10",
      "material": "Polypropylene",
      "us_duty_pct": 6.5,
      "canada_duty_pct": null,
      "unit_cost_usd": 3.45,
      "map_price": 19.99,
      "moq": 500,
      "is_hero": true,
      "is_hero_for_ads": false,
      "season": null,
      "status": "active",
      "tags": ["bestseller", "prime"],
      "weight": 0.35,
      "weight_uom": "kg",
      "units_per_case": 24,
      "cases_per_pallet": 60,
      "unpacked_length_cm": 12.0,
      "unpacked_width_cm": 8.0,
      "unpacked_height_cm": 5.0,
      "unpacked_net_weight_kg": 0.30,
      "packaged_length_cm": 14.0,
      "packaged_width_cm": 10.0,
      "packaged_height_cm": 7.0,
      "packaged_gross_weight_kg": 0.35,
      "created_at": "2026-01-15T10:30:00.000000Z",
      "updated_at": "2026-04-20T14:22:00.000000Z"
    },
    "links": {
      "self": "https://ledgeroto.com/api/v1/skus/019dc4b7-..."
    }
  }
}

Vendors

Manage your supplier/vendor directory.

List Vendors

GET /api/v1/vendors

Scope: vendors:read

Create Vendor

POST /api/v1/vendors

Scope: vendors:write — Returns 201 Created

FieldTypeDescription
name REQUIREDstringVendor name (max 255)
fob_portstringFOB port of export (max 255)
pmt_1_pctnumericPayment 1 percentage (0–100)
pmt_2_pctnumericPayment 2 percentage (0–100)
pmt_3_pctnumericPayment 3 percentage (0–100)

Get Vendor

GET /api/v1/vendors/{id}

Scope: vendors:read

Update Vendor

PUT /api/v1/vendors/{id}

Scope: vendors:write

Delete Vendor

DELETE /api/v1/vendors/{id}

Scope: vendors:write — Returns 204 No Content

Locations

Manage physical and logical locations used in inventory tracking and shipment routing.

List Locations

GET /api/v1/locations

Scope: locations:read

Create Location

POST /api/v1/locations

Scope: locations:write — Returns 201 Created

FieldTypeDescription
code REQUIREDstringShort code, e.g. FBA-LAX4 (max 255)
name REQUIREDstringLocation name (max 255)
type REQUIRED string One of: factory, in_transit, freight_forwarder, fba, awd, third_party_logistics, own_warehouse
address_line_1stringStreet address (max 255)
address_citystringCity (max 255)
address_countrystringISO 3166-1 alpha-2 country code (max 2)

Get Location

GET /api/v1/locations/{id}

Scope: locations:read

Update Location

PUT /api/v1/locations/{id}

Scope: locations:write

Delete Location

DELETE /api/v1/locations/{id}

Scope: locations:write — Returns 204 No Content

Manufacturers

Manage manufacturer records.

List Manufacturers

GET /api/v1/manufacturers

Scope: manufacturers:read

Create Manufacturer

POST /api/v1/manufacturers

Scope: manufacturers:write — Returns 201 Created

FieldTypeDescription
name REQUIREDstringManufacturer name (max 255)

Get Manufacturer

GET /api/v1/manufacturers/{id}

Scope: manufacturers:read

Update Manufacturer

PUT /api/v1/manufacturers/{id}

Scope: manufacturers:write

Delete Manufacturer

DELETE /api/v1/manufacturers/{id}

Scope: manufacturers:write — Returns 204 No Content

Warehouses

Manage warehouse records.

List Warehouses

GET /api/v1/warehouses

Scope: warehouses:read

Create Warehouse

POST /api/v1/warehouses

Scope: warehouses:write — Returns 201 Created

FieldTypeDescription
name REQUIREDstringWarehouse name (max 255)

Get Warehouse

GET /api/v1/warehouses/{id}

Scope: warehouses:read

Update Warehouse

PUT /api/v1/warehouses/{id}

Scope: warehouses:write

Delete Warehouse

DELETE /api/v1/warehouses/{id}

Scope: warehouses:write — Returns 204 No Content

Carriers

Manage shipping carriers (freight forwarders, logistics providers).

List Carriers

GET /api/v1/carriers

Scope: carriers:read

Create Carrier

POST /api/v1/carriers

Scope: carriers:write — Returns 201 Created

FieldTypeDescription
name REQUIREDstringCarrier name (max 255)

Get Carrier

GET /api/v1/carriers/{id}

Scope: carriers:read

Update Carrier

PUT /api/v1/carriers/{id}

Scope: carriers:write

Delete Carrier

DELETE /api/v1/carriers/{id}

Scope: carriers:write — Returns 204 No Content

Shipments

Manage shipments between locations. Shipments track the movement of goods from factories to freight forwarders to Amazon fulfillment centers.

List Shipments

GET /api/v1/shipments

Scope: shipments:read

Create Shipment

POST /api/v1/shipments

Scope: shipments:write — Returns 201 Created

FieldTypeDescription
carrier_iduuidFK to a carrier record
from_location_iduuidFK to origin location
to_location_iduuidFK to destination location
ship_methodstringOne of: ocean, air, truck, rail, courier
shipped_atdateDeparture date (ISO 8601)
arrived_atdateArrival date (ISO 8601)
expected_delivery_datedateExpected delivery date
estimated_costintegerEstimated cost in cents
actual_costintegerActual cost in cents
notesstringFree-text notes (max 65,535 chars)

Get Shipment

GET /api/v1/shipments/{id}

Scope: shipments:read

Update Shipment

PUT /api/v1/shipments/{id}

Scope: shipments:write

Delete Shipment

DELETE /api/v1/shipments/{id}

Scope: shipments:write — Returns 204 No Content

Response 200 (Get / List / Update)

{
  "data": {
    "id": "019dc4b7-...",
    "type": "shipments",
    "attributes": {
      "ship_number": "SH-0042",
      "ship_method": "ocean",
      "shipped_at": "2026-03-01T00:00:00.000000Z",
      "arrived_at": null,
      "expected_delivery_date": "2026-04-15",
      "estimated_cost": 450000,
      "actual_cost": 0,
      "notes": "Container MSKU1234567",
      "carrier_id": "019dc4b7-...",
      "from_location_id": "019dc4b7-...",
      "to_location_id": "019dc4b7-...",
      "created_at": "2026-02-28T09:15:00.000000Z",
      "updated_at": "2026-03-01T12:00:00.000000Z"
    },
    "relationships": {
      "carrier": { "data": { "id": "019dc4b7-...", "type": "carriers" } },
      "from_location": { "data": { "id": "019dc4b7-...", "type": "locations" } },
      "to_location": { "data": { "id": "019dc4b7-...", "type": "locations" } }
    },
    "links": {
      "self": "https://ledgeroto.com/api/v1/shipments/019dc4b7-..."
    }
  }
}