Skip to content
BolderBolder Developers
Esc
↑↓navigate↵open⌘Jpreview
On this page

Bolder API v2

Getting Started

The Bolder API provides programmatic access to your Bolder Shop data through a hypermedia-driven REST interface.

Fastest path — Personal Access Token

For scripts, CLI tools, and personal automation, skip the OAuth grant dance entirely:

  1. Go to https://auth.onbolder.com/dev/credentials and sign in. The first visit auto-creates a default developer application for your account — there’s nothing to register.
  2. Under Personal Access Tokens, create a new token: pick the scopes you need (see Token scopes below) and an expiry.
  3. Copy the token — it’s shown once.
export BOLDER_TOKEN=PASTE_YOUR_TOKEN_HERE

curl -H "Authorization: Bearer $BOLDER_TOKEN" https://api.onbolder.com/v2

All curl examples in this documentation use $BOLDER_TOKEN. Set it once in your shell and every example works immediately. See Personal Access Tokens below for details on expiry and revocation.

Building a real integration — register an OAuth application

If you’re building something you’ll distribute to other Bolder merchants (rather than automating your own account), register a dedicated application instead of using a PAT:

Go to https://auth.onbolder.com, sign in, and create a new application. You’ll receive a CLIENT_ID and CLIENT_SECRET. The fastest way to get a token from a terminal is the Client Credentials grant:

export BOLDER_TOKEN=$(curl -s \
  -X POST https://auth.onbolder.com/oauth/token \
  -d "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&scope=products.read,orders.read,orders.write" \
  | jq -r .access_token)

No jq? Use Python instead:

export BOLDER_TOKEN=$(curl -s \
  -X POST https://auth.onbolder.com/oauth/token \
  -d "grant_type=client_credentials&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&scope=products.read,orders.read,orders.write" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

Note: Unlike PATs, these tokens are temporary. Re-run the request above when your token expires, or use the refresh_token grant described in the Authentication section below.


Authentication

The Bolder API uses OAuth 2.0. The token must be sent in the Authorization header — query-parameter tokens are not accepted by v2.

Five grant types are supported:

Redirect the user to the authorization endpoint and exchange the returned code for tokens on your server.

GET https://auth.onbolder.com/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=products.read,orders.read,orders.write

Then exchange the code:

POST https://auth.onbolder.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&redirect_uri=https://yourapp.com/callback
&code=RETURNED_CODE

2. Implicit Grant (for client-side JavaScript apps)

GET https://auth.onbolder.com/oauth/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://yourapp.com/callback
  &response_type=token
  &scope=products.read

The access token is returned in the URL fragment after the redirect.

3. Client Credentials (for server-to-server / automated scripts)

No user involved — the application authenticates as itself.

POST https://auth.onbolder.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&scope=products.read,orders.read,orders.write

4. Resource Owner Password Credentials (trusted apps only)

POST https://auth.onbolder.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=password
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&username=USER_EMAIL
&password=USER_PASSWORD
&scope=products.read,orders.read,orders.write

5. JWT Bearer (refreshing expired tokens)

Exchange a refresh_token for a new access token:

POST https://auth.onbolder.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET
&refresh_token=YOUR_REFRESH_TOKEN

Token response

All flows return a JSON token response:

{
  "access_token":  "eyJ...",
  "token_type":    "bearer",
  "expires_in":    7200,
  "refresh_token": "abc123...",
  "scope":         "products.read,orders.read,orders.write"
}

Access tokens are temporary. Implement refresh-token logic to obtain new tokens before they expire.

Token scopes

v2 uses granular scopes: request access to individual resource namespaces using {namespace}.read / {namespace}.write scopes. This lets an integration ask for only what it needs — e.g. an order-fulfillment tool can request orders.read,orders.write without also getting product or customer write access.

Namespace Read scope Write scope Covers
products products.read products.write Products, variants, collections
orders orders.read orders.write Orders, documents, tracking
customers customers.read customers.write Customer profiles
shops shops.read shops.write Shop configuration
hub hub.read hub.write Content (posts, pages)
store store.read store.write Themes and assets
sellers sellers.read — (read-only) Seller information
batches batches.read batches.write Batch job status

Request them the same way as any other scope, comma-separated:

&scope=products.read,orders.read,orders.write

Personal Access Tokens (PATs)

For scripts and CLI tools where the standard OAuth grants (which require redirect flows or a client secret exchange per session) are overkill, you can issue a Personal Access Token: a long-lived Bearer JWT tied to your user account and a specific application, created from the developer credentials page — see Fastest path — Personal Access Token above for the step-by-step. A few more details:

  • Give the token a name, choose an expiry (30 days, 90 days, 1 year, or no expiry), and select the scopes to grant — you can only grant scopes your own user role already has (see Token scopes above).
  • The raw token is shown once, immediately after creation. Copy it then — it cannot be retrieved again afterwards.

Use it exactly like any other access token:

curl -H "Authorization: Bearer $PERSONAL_ACCESS_TOKEN" https://api.onbolder.com/v2

PATs do not expire on a fixed short TTL like regular access tokens, so there is no refresh-token flow — the token is valid until its chosen expiry (if any) or until revoked. Revoke a PAT at any time from the same credentials page; revocation takes effect immediately.


Hypermedia (HAL)

All responses follow the HAL (Hypertext Application Language) format. Every response contains:

  • _links — navigation links to related resources and available actions.
  • _embedded — inline sub-resources (e.g. variants inside a product).
  • _class — array of type strings (e.g. ["results", "products"]).
  • Properties — the resource’s own data fields at the top level.

CURIEs

Link relation types use service-namespaced CURIEs so clients can resolve documentation for any relation:

{
  "_links": {
    "curies": [
      { "name": "products", "href": "https://api.onbolder.com/docs/v2#products:{rel}", "templated": true },
      { "name": "orders",   "href": "https://api.onbolder.com/docs/v2#orders:{rel}",   "templated": true }
    ],
    "self":             { "href": "https://api.onbolder.com/v2/products/123" },
    "products:update":  { "href": "https://api.onbolder.com/v2/products/123", "method": "put" },
    "products:variants":{ "href": "https://api.onbolder.com/v2/products/123/variants" }
  }
}

Clients should follow links rather than constructing URLs. If a link is absent from a response, the action is either unavailable or not permitted for the current token. The presence of a link is itself an affordance.

URI Templates

Some links are URI templates (marked "templated": true). Expand them with the parameters shown in the template before making a request:

products:list → https://api.onbolder.com/v2/products{?q,status,page,per_page,...}

Expanded:       https://api.onbolder.com/v2/products?status=visible&page=2

Pagination

List endpoints return a standard pagination envelope:

{
  "_class":      ["results", "products"],
  "total_items": 142,
  "per_page":    20,
  "page":        1,
  "_links": {
    "self": { "href": "https://api.onbolder.com/v2/products?page=1" },
    "next": { "href": "https://api.onbolder.com/v2/products?page=2" },
    "prev": { "href": "https://api.onbolder.com/v2/products?page=0" }
  },
  "_embedded": {
    "items": [...]
  }
}

Default page size is 20, maximum is 200 (use per_page to override).


Errors

HTTP Status Meaning
400 Bad request — malformed JSON or missing required field
401 Unauthorized — missing or invalid access token
403 Forbidden — valid token but insufficient permissions
404 Not found
422 Unprocessable — validation failed; see _embedded.errors
429 Too many requests — rate limit exceeded
500 Server error

Validation errors (422) embed a structured error list:

{
  "_embedded": {
    "errors": [
      { "field": "name", "messages": ["can't be blank"] }
    ]
  }
}

HTTP caching

The API emits standard ETag and Last-Modified headers. Clients may send If-None-Match / If-Modified-Since on subsequent requests; unchanged resources return 304 Not Modified with no body.

Version 2.0
Base URLhttps://api.onbolder.com/v2

Root

API entry point — embeds sellers, shops, and navigation links to every resource

Sellers

Seller entities — the business layer between an account and its shops. An account can have multiple sellers, each owning one or more shops and their product catalog.

Shops

Shops owned by the authenticated account. A shop is the customer-facing storefront that belongs to a seller. Settings, admins, shipping tables, payment methods, exchange rates, and other shop-level configuration are anchored to a specific shop.

Content

CMS-style content for a shop’s storefront — blogs and their posts, static pages, and contact/lead-capture forms. A shop can have several blogs, each with its own language; a post in one language can point at the post it translates.

Products

Catalog products, automatically scoped to the authenticated account’s sellers. Account-scoped tokens need no extra parameters; tokens without an account/seller scope must supply shop_subdomains.

Variants

Product variants (size, colour, etc.). Nested under /products/{product_id}/variants. Each variant holds its own SKU, price, stock level, and images.

Price Lists

Named price lists for B2B / wholesale pricing. Products can have per-variant price overrides within a price list.

Product Types

Product type definitions used to group and filter catalog products.

Collections

Curated product collections (manually or rule-based). Products can belong to multiple collections.

Orders

Shop orders. Supports batch status updates and per-order document management.

Customers

Shop customers. Includes customer authentication and password-reset flows.

Promotions

Promotional discount codes and automatic discounts applied at checkout.

Volume Discounts

Quantity-based discount rules (tiered pricing by units purchased).

Themes

Hosted-storefront themes, templates, and assets. Applies only to shops with a hosted storefront (store: namespace); absent for headless shops.

Webhooks

Event webhooks. Subscribe to events such as order.created, product.updated, customer.created, etc. Inactive subscriptions can be reactivated and their delivery history inspected.

Topic spelling

The v2 API spells topics in the singular: customer.created, order.updated.item.added, product.updated.variant.sold_out. Only the nouns change; verbs and states (updated, sold_out, opted_out) stay as they are. The singular spelling is used everywhere in v2: the topic of a subscription, the topic of the notifications it receives, and the topic of events returned by /events.

On input the plural spelling (customers.created) is accepted too, in the topic of a new subscription and in the topic filter of /events. A subscription created through v2 always receives the singular spelling, whichever one you sent. Subscriptions created before this change, and those created through the v1 API, keep receiving the plural spelling so their consumers are not affected: every subscription reports the spelling it receives in topic_style (singular or plural).

Topic hierarchy

Topics are dot-separated and hierarchical. A subscription to a topic also receives every topic below it: order receives all order events, order.updated receives order.updated.placed, order.updated.closed, and so on, while order.updated.closed receives only that one. Sibling topics are independent.

Customer events

Customer event topics use the singular customer:

Topic Sent when
customer.created A customer is created, whichever way it happens: the API, a storefront sign-up, a newsletter or contact form, checkout, social login, or the admin.
customer.updated A customer’s profile data changes (name, email, phone, company, custom fields, opt-in, …). Automatic bookkeeping does not count: order totals and counts, last login, timestamps and integration sync state never trigger it.
customer.opted_in An existing customer opts in to email marketing. Sent together with customer.updated.
customer.opted_out An existing customer opts out of email marketing. Sent together with customer.updated.
customer.deleted A customer is permanently deleted. The payload is minimal (the customer’s id, since the record no longer exists).

Customer payloads never include the customer’s activation token or the links that embed it.

A brand-new customer who opts in at sign-up only produces customer.created; check opt_in in its payload. Internal placeholder customers (for example the shared “Venta Presencial” customer used for point-of-sale sales without an identified buyer) never produce events.

The payload of a customer.* webhook is the customer representation, the same one returned by GET /customers/{id} (see the Contact schema). Events are delivered asynchronously, usually within seconds of the change, and delivery order between different changes is not guaranteed: use updated_on to order updates to the same customer.

Subscribe to customer to receive all customer events, or to a specific topic to receive only that one. customer.updated does not include customer.opted_in or customer.opted_out, which are separate topics; a subscription to customer receives both events for an opt-in change.

Change to existing customers.created / customers.updated webhooks: their payload now uses the v2 customer representation. The deprecated company property is gone (use company_name) and links point to v2 URLs (/v2/customers/{id}). Their topic keeps the plural spelling unless the subscription was created through v2 (see Topic spelling).

Product stock and price events
Topic Sent when
product.sold_out A product is no longer available in a stock location (its last available variant ran out there).
product.restocked A product is available again in a stock location.
product.updated.variant.sold_out A single variant ran out in a stock location.
product.updated.variant.restocked A single variant is available again in a stock location.
product.updated.variant.low_stock A variant’s stock fell to the account’s minimum (critical_stock_amount) or below, without being sold out. Also sent when a sold out variant comes back with that little stock.
product.updated.variant.replenished A variant’s stock went back above the account’s minimum.
product.price_dropped A product’s final price went down (its sale price if it has one, otherwise its regular price).
product.price_raised A product’s final price went up.
product.updated.variant.price_dropped A variant’s final price went down.
product.updated.variant.price_raised A variant’s final price went up.

The old and new price are in the event’s changes: {"price": [1000, 800], "sale_price": [1000, 800]}, in the currency’s smallest unit (for CLP, whole pesos). A change that leaves the final price as it was, such as raising the regular price while a sale price applies, sends nothing.

A product’s price derives from its variants, so one price change can produce both a variant event and a product event. Which of the two you receive depends on how the change was made, and a change made through the product form of a single-variant product may only produce one of them: subscribe to both product.price_dropped and product.updated.variant.price_dropped (and the raised pair) if you must not miss a price change.

Refund events
Topic Sent when
refund.created A refund was accepted by the payment gateway (successful). A refund that failed first and later succeeded is announced when it succeeds.
refund.updated A successful refund changed afterwards (for example its description).
refund.failed The payment gateway rejected a refund. Sent once per attempt; a later refund.created for the same order is the retry succeeding.

The payload is the refund transaction (order_id, negative amount, absolute_amount, payment_method_type, transaction_id, …). The raw gateway reply is never included. Refund topics are not part of the order family: subscribe to refund. A token needs orders.read.

Webhook events
Topic Sent when
webhook.disabled One of your webhooks stopped being delivered: the receiver answered 410 Gone, or too many deliveries failed. The payload names the host only.
webhook.reactivated A stopped webhook was reactivated (PUT /webhooks/:id/reactivate) or subscribed again.
Cart events
Topic Sent when
cart.updated The cart of an identified customer changed: items, quantities or total.
cart.deleted That cart was emptied.

A cart is a draft order, so the payload is the order representation. Cart events are only sent for carts that belong to an identified customer (a logged-in customer on the storefront), and only when something actually changed. Cart topics are not part of the order family: subscribing to order does not include them, subscribe to cart. A token needs orders.read to subscribe.

Collection events
Topic Sent when
collection.created A collection is created.
collection.updated A collection’s title, slug, description, visibility or other data changes. Adding or removing products (the product counter) does not count.
collection.deleted A collection is permanently deleted. The payload is minimal (the id).

Collections belong to the whole account, not to one shop, so these events are recorded under the account’s main shop. A token needs products.read to subscribe.

Integration events
Topic Sent when
integration.misconfigured A shop’s integration (an add-on such as a mailing list, analytics or Facebook) stopped working on the merchant’s side: a required option is missing, or the provider rejected its credentials. Sent once per breakage, not once per failed call.
integration.recovered That integration worked again after being misconfigured.

The payload has no body; the event’s payload carries the add-on key (integration, eg. google_analytics) and, for integration.misconfigured, the reason (options or credentials). Subscribe to integration for both. Only an admin token can subscribe.

Alerts

The notification feed of the admin (the bell): things a merchant should look at or act on. The kinds are defined by the platform, they are not configured per account. Each one is raised by a webhook topic:

topic Raised when Leaves the feed when
product.sold_out A product is no longer available. It is restocked (product.restocked).
integration.misconfigured An integration (add-on) stopped working on the merchant’s side: a required option is missing, or the provider rejected its credentials. The integration works again (integration.recovered).

Every open alert is in the feed for every user who can see the seller. Seen and read are per user, like a bell: opening the feed marks what it shows as seen (the badge, unseen_count), reading an alert is explicit and can be undone. One user reading an alert does not change it for the others. Alerts are open or resolved: only open ones are listed. Who is emailed about an alert is decided by the notification profile of each recipient, which opts in to the topics it wants (GET /alerts/topics lists the ones it can opt in to). Every endpoint needs shops.read; all but /alerts/topics also need a token issued to a user.

Subscriptions

Recurring product subscriptions. SubscriptionFee defines the recurring payment terms attached to a product/variant; SubscriptionPurchase is a customer’s active subscription created from a recurring order.

Wallets

Points and store-credit wallets, one per contact per program. A seller runs at most one active PointsProgram and one active CreditProgram, each covering every shop under the seller. Use register_purchase / unregister_purchase to award or reverse points for completed orders. Credit wallets carry a spending limit and accrue payments toward it.

Catalog

Store

Was this page helpful?