Activity Log API
Read your store’s activity log: who changed what, how, and when, whether in the dashboard or through the API. Every change a person or API key makes is logged; only automatic changes, such as stock a checkout takes, stock a refund puts back, and Easel’s background jobs, are not. It is the same log the dashboard shows under Settings › Activity and as the History on each record’s page.
Events are read-only. Easel records each one in the same transaction as the change it describes and keeps it until the store is deleted; a correction is a later event. Orders keep their own history, read with List Order Events, and gift cards theirs in their transactions, so an order’s shipments and refunds and a gift card’s balance aren’t repeated here.
Endpoints
- List Audit Events -
GET /api/v1/audit-events/
List Audit Events
Retrieve a page of the store’s activity log, newest first.
Endpoint: GET /api/v1/audit-events/
Scope Required: audit_events:read
Query Parameters
| Parameter | Type | Description |
|---|---|---|
after |
string | Cursor for pagination. Event ID to return older events after |
before |
string | Cursor for pagination. Event ID to return the newer events before, for paging back |
limit |
integer | Number of events to return (default 10, max 100) |
entity_type |
string | Only changes to this kind of record (see Entity Types) |
entity_id |
string | Only changes to this one record, such as a product’s public ID. Requires entity_type |
actor |
string | Only changes made by this dashboard user or API key, named by the actor.id of one of its events. A platform’s staff can’t be filtered by |
Events come newest first either way. The response reports has_more, which
is true when more events follow in the direction of travel: older ones, or
newer ones with before.
To read one product’s history, pass its ID with its type:
GET /api/v1/audit-events/?entity_type=product&entity_id=pro_d0b6db728q6vrspbvbqg
Request
curl -X GET "https://studio.easel.engineering/api/v1/audit-events/?entity_type=product&limit=3" \
-H "Authorization: Bearer your_api_key_here"
Response
Status: 200 OK
{
"data": [
{
"id": "aud_d0b6db728q6vrsaud006",
"entity": {
"type": "product",
"id": "pro_d0b6db728q6vrspbvbqg",
"name": "Cozy Blanket"
},
"action": "edited",
"actor": {
"type": "api_key",
"id": "api_devseedkey0000000001",
"name": "Inventory sync"
},
"details": {
"changes": [{ "field": "description" }]
},
"created_at": "2025-05-03T19:06:40Z"
},
{
"id": "aud_d0b6db728q6vrsaud005",
"entity": {
"type": "product",
"id": "pro_d0b6db728q6vrspbvbqg",
"name": "Cozy Blanket"
},
"action": "edited",
"actor": {
"type": "user",
"id": "usr_d0b6cu728q6vrspbvbag",
"name": "Ada"
},
"details": {
"changes": [{ "field": "stock", "from": "30", "to": "42" }],
"variant": "Green, Small",
"source": "inventory"
},
"created_at": "2025-05-03T19:05:00Z"
},
{
"id": "aud_d0b6db728q6vrsaud004",
"entity": {
"type": "product",
"id": "pro_d0b6db728q6vrspbvbqg",
"name": "Cozy Blanket"
},
"action": "edited",
"actor": { "type": "user", "id": null, "name": null },
"details": {
"changes": [{ "field": "price", "from": "2499", "to": "2999" }],
"variant": "Green, Small"
},
"created_at": "2025-05-03T18:55:52Z"
}
],
"has_more": true
}
The last event was made by a dashboard user who has since been removed, so
its actor has no id or name.
Error Responses
Invalid parameter
Status: 400 Bad Request
A malformed after or before, both at once, an invalid limit, an
unknown entity_type, an entity_id without entity_type, or an actor
that isn’t one of the store’s users or API keys returns
INVALID_REQUEST_ERROR, with the offending parameter named in the error
message:
{
"error": "Invalid 'entity_type' parameter: must be a kind of record the log holds",
"code": "INVALID_REQUEST_ERROR"
}
Unknown cursor
Status: 404 Not Found
An after or before that names no event of the store:
{
"error": "Resource not found",
"code": "NOT_FOUND"
}
Audit Event Object
| Field | Type | Description |
|---|---|---|
id |
string | Event public ID |
entity |
object | The record the change was made to (see below) |
action |
string | What happened to it (see Actions) |
actor |
object | Who made the change (see below) |
details |
object | Fields that depend on action and the record; fields that don’t apply are left out (see Details) |
created_at |
string | ISO 8601 timestamp |
Entity Object
| Field | Type | Description |
|---|---|---|
type |
string | The kind of record (see Entity Types) |
id |
string | The record’s ID, the value entity_id filters by |
name |
string | The record’s name when the change was made, so the log still reads well after it’s renamed or deleted |
Actor Object
| Field | Type | Description |
|---|---|---|
type |
string | user (a dashboard user), api_key, system (Easel itself), or platform (on a hosted platform, its staff, from their staff console or in your dashboard with support access) |
id |
string|null | The user’s, API key’s, or platform staff member’s public ID; for a user or key, the value actor filters by. null for Easel itself, for a platform change no staff member is named for, or once that user or key is deleted |
name |
string|null | The user’s, API key’s, or platform staff member’s name; null whenever id is |
Entity Types
| Type | Record | entity.id |
Actions |
|---|---|---|---|
product |
A product, its variants, options and their choices, images, digital files, tags, sites, and reviews | Product ID | created, duplicated, edited, added, removed, replaced, moved, hidden, unhidden, taken_down, restored |
tag |
A tag | Tag ID | created, edited, deleted |
collection |
A collection and its tags | Collection ID | created, edited, deleted, added, removed, taken_down, restored |
store_filter |
A site’s storefront filter, named by the option name it filters by | Store filter ID | created, edited, moved, deleted |
page |
A site’s home page or information page, such as its returns policy | Site ID and page type, such as sit_d0b6fmv28q6vn14peun1/returns, or /home for the home page |
created, edited, deleted, taken_down, restored |
gift_option |
A site’s gift option | Gift option ID | created, edited, moved, deleted |
discount_code |
A discount code | Discount code ID | created, edited, deleted |
gift_card |
A gift card’s expiry and note, named by the last four characters of its code | Gift card ID | edited |
customer |
A customer and their addresses, with no name (see Personal Data) | Customer ID | created, edited, deleted, added, removed |
shipping_rate |
A shipping rate | Shipping rate ID | created, edited, moved, deleted |
tax_registration |
A tax registration, named by its place, such as US-CA |
Tax registration ID | created, edited, deleted |
location |
A location, named by its name | Location ID | created, edited, deleted |
site |
A site’s settings, including its languages, translations, gift notes, tax, and review settings | Site ID | created, edited |
payment_key |
The store’s Stripe keys | stripe |
created, replaced, deleted |
shipping_key |
The store’s ShipStation key | shipstation |
created, replaced, deleted |
store_user |
A person on the store’s dashboard team, and their passkeys | User ID | invited, joined, edited, deleted, added, removed, signed_in, reset |
store |
The store’s own settings, such as requiring passkeys, and, on a hosted platform, its plan and the platform’s moderation of the store and of its reviews and images | Store ID | edited, plan_changed, billed, locked, unlocked, suspended, unsuspended, taken_down, restored |
api_key |
An API key | API key ID | created, deleted |
webhook_endpoint |
A webhook endpoint, named by its URL | Webhook endpoint ID | created, edited, deleted, replaced, pinged, redelivered |
staff_access |
On a hosted platform, support access the store granted the platform’s staff, named by how long it lasts once used: 2h, 24h, 1w, or 1m |
Support access ID | created, redeemed, opened, revoked, voided, ended, expired |
export |
A request for a copy of the store’s data, which holds its customers’ personal data | Export ID | created |
New entity types may be added, so ignore types you don’t recognize.
Actions
created- The record was made. For a payment or shipping key, the key was connectedduplicated- The product was made as a copy of another, whose ID is insource_idand name innameedited- The record changed; each changed field is inchangesdeleted- The record was deleted. For a key, it was disconnected; for a store user, they were removed from the storeinvited- A person was invited to join the store’s dashboard; the role they’ll join with is inchangesasrolejoined- The invited person accepted and joined the store; they are the event’s actorreplaced- A payment or shipping key was replaced with a new one, a webhook endpoint’s signing secret was rotated, or a variant’s digital file was replaced with another (partisdigital_file, andchangesholds the old and new file names)addedandremoved- A part of a record, named bypartandname, was added or removed, such as a product’s variant or a collection’s tagmoved- The record, or the partpartandnamesay, moved one place up or down its list, asdirectionsayshiddenandunhidden- One of a product’s reviews was hidden from shoppers or shown again (partisreview)signed_in- A store user logged in to the dashboard with the passkeynamenames (partispasskey); they are the event’s actor, in the store their session openedreset- Every passkey of a store user who lost theirs was removed at once (partispasskey), and they were logged out, by a teammate or by the platform’s support. Passkeys belong to the person rather than a store, so each store they’re on logs itpinged- A test event was sent to the webhook endpointredelivered- A failed delivery, named by its ID inname, was sent again to the webhook endpoint
Only a store on a hosted platform, where the platform’s staff run its plan, moderation, and support, logs these:
redeemed- A platform staff member used the store’s support access code; they are the event’s actoropened- The platform staff member using the store’s support access opened another way into its dashboard, such as from another browser; they are the event’s actorrevoked- The store’s owner ended its support access, or made a new code in its placevoided- The support access code was guessed wrong too many times, so it no longer works; the actor is the staff member whose guess voided itended- The platform staff member ended their support access, done helping, or the platform’s operator ended it as they removed the staff member’s sign-in; the actor is whoever ended itexpired- The support access code lapsed unused, or the access ran out; the actor issystemtaken_downandrestored- The platform’s staff removed content as illegal or against its terms, or put it back (see Moderation). For a review or image, the entity is the store, andpartisrevieworimage, with an image’s ID inname;reasonsays whyplan_changed- The platform’s billing changed the store’s plan: its tier, where it stands, or when its paid period ends, each inchangesassubscription_tier(by name) andsubscription_tier_id(by ID),billing_status, andpaid_until. Only what changed is listed, and a plan set as it was logs nothing. The actor is the member of the platform’s staff named, or Easel itself, with no namebilled- Only in older events: the platform’s staff charged the store for its plan on demand; the actor is the staff memberlockedandunlocked- The platform locked the store for its plan, its storefront and API offline until it’s unlocked, or unlocked it;reasonsays why it was locked:billing, for a plan gone unpaid, ortrial_ended. The actor is the member of the platform’s staff who did it, or Easel itself, with no name. A lock the platform’s periodic job makes, once the store’s trial ended or its paid period and any grace ran out, has actorsystemsuspendedandunsuspended- The platform’s staff suspended the store for its content, or lifted the suspension;reasonsays why it was suspended
New actions may be added, so ignore actions you don’t recognize.
Details
| Field | Description |
|---|---|
changes |
For edited, each field that changed, as field, from, and to (see below); for a store user invited, the role they’ll join with, in to |
part |
What kind of part an event is about: variant, option, option_value, image, digital_file, tag, site, review, address, or passkey |
name |
The part’s name, such as the variant Large, a choice as Size: Large, or a digital file’s file name, or the name a store user gave a passkey; for duplicated, the product copied; for redelivered, the delivery’s ID. Reviews and addresses are never named |
variant |
The variant a change is for, such as to its price, stock, image, or digital file; left out for a product without options |
direction |
For moved, up or down |
source |
Where stock was changed: variant (the variant’s own page) or inventory (the inventory page); left out for the product’s page or the API |
source_id |
For duplicated, the ID of the product copied |
previous |
For a page edited or deleted, its text before the change, when it was no longer than 8,000 bytes; for the home page, its main text |
reason |
For taken_down or suspended, why: illegal, csam (child sexual abuse material), ip (intellectual property), fraud, hate (hate or violence), terms (against the platform’s terms), or other; for locked, billing or trial_ended |
Each change’s field names what changed, and from and to are its old
and new values as strings. Either is left out when the field was unset, and
both are left out for a field logged without its values, such as a product’s
description, a site’s logo, its postal address, or any of a customer’s
fields. Values longer than 200 characters are cut short with …. Prices are
in the store’s smallest currency unit, weights in grams, dates as
YYYY-MM-DD, images by their public IDs, and true or false settings are
true or false.
| Record | Fields |
|---|---|
| Product | name, slug, description, status, made_to_order, handling_days, weight, price, sale_price, stock, sku, barcode, option (an option’s name), option_value and swatch (a choice’s name and its color as hex, with the choice in name), image (a variant’s image), alt_text (an image’s description), digital_file (a file name) |
| Tag | name |
| Collection | name, description |
| Store filter | name, position |
| Home page | hero_content (its main text, logged as changed), image, social_image (the image shown when it’s shared), seo_title, seo_description |
| Gift option | name, description, price, offered (offered at checkout), image, position |
| Gift card | expires, note |
| Customer | name, email |
| Discount code | code, percentage, active |
| Shipping rate | name, base_price, transit, surcharge_percent, free_over |
| Tax registration | tax_rate (a percent, left out for the built-in rate), tax_id |
| Location | name, place (its country and region, such as US-IL), sites (the names of the sites offering it) |
| Site | name, copyright_name, site_status, language, languages (its other languages’ codes), translations (the code of a language whose translations were saved, in to), domain, logo, favicon, shipping_countries (the ISO 3166-1 alpha-2 codes of the countries it ships to, such as CA, US), sale_alerts, reviews, cancel_window, handling_days, postal_address, gift_notes, gift_note_length, home_country (its country’s ISO 3166-1 alpha-2 code), price_mode (only in older events; it now follows home_country) |
| Store user | name, email, role (owner or manager) |
| Webhook endpoint | url, description, events (the events it’s sent, empty for all), active |
| Store | require_passkeys (every dashboard user logs in with a passkey after their password), and, on a hosted platform, subscription_tier (the store’s plan, by name, which the platform’s staff change), subscription_tier_id (the same plan by its ID, which tells apart two of one name), billing_status (where the plan stands: trialing, active, past_due, canceled, or free), paid_until (when the period the store paid for ends, RFC 3339, empty for none) |
New fields may be added, so ignore fields you don’t recognize.
Moderation
On a hosted platform, the platform hosts what your store publishes, so
under the EU Digital Services Act its staff review it and may remove
content that’s illegal or against its terms, or suspend the store. Each
such decision is logged with the platform actor naming the staff member,
and the store’s owners are emailed a statement of the reasons; the
dashboard lists them under Settings › Activity › Easel decisions. Content
the platform took down has its taken_down_at set, the time it was taken
down, and can’t be published or shown again through the API either; see
each resource’s errors.
Support Access
On a hosted platform, its staff have no standing access to your dashboard. To let them in to help, the store’s owner makes an 8-digit code under Settings › Support access and reads it to them. A staff member redeems it in the platform’s staff console, for your store only, within the hour, and may then use your dashboard for the time the owner chose, until the owner revokes it. They can’t manage your team, API keys, payouts, billing, or exports.
Each change they make is logged with actor.type platform, naming the
staff member in actor.id and actor.name, and the access itself is
logged as a staff_access record from the code’s creation to its end.
Personal Data
The log is kept as long as the store, past a customer’s erasure, so it holds no customer’s personal data. A customer is named only by their ID, their fields are logged as changed without their values, their addresses are never described, and a review hidden or shown again isn’t quoted.
Secrets
The log never holds a secret. A payment or shipping key’s events say only
that it was connected (created), replaced, or disconnected (deleted),
and an API key’s events never carry the key itself. A webhook endpoint’s
signing secret is never logged, only that it was replaced.