Create Aggregate
Provision a new sub-tenant (aggregate) under your aggregator store. The new tenant is created with its own domain, admin user, and store configuration.
Endpoint
POST /api/v3/aggregates
Request Body
Required Fields
| Field | Type | Description |
|---|---|---|
name | string | Store name (max 255 characters) |
lang | string | Store language. Must be one of the supported codes (e.g., en, es, pt). |
timezone | string | Store timezone (IANA format, e.g., America/New_York) |
support_email | string | Support email address (max 255 characters) |
Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
final_domain | string | Auto-generated from name as slug | Store domain (must be unique, max 255 chars) |
external_id | string | null | Your own opaque identifier for the sub-tenant (max 255 chars) |
primary_color | string | null | Primary brand color (hex, e.g., #336699) |
secondary_color | string | null | Secondary brand color (hex, e.g., #FF6600) |
reader_accent_color | string | null | Reader accent color used in the reader's light and dark themes (hex, e.g., #0066CC) |
reader_flashcards_enabled | boolean | false | When true, signed-in readers of the store can create flashcards from text selections in PDF and EPUB products. Never offered to anonymous readers or on audiobooks. Off by default; the aggregator's own setting is not inherited. |
logo_url | string | null | Public URL of the store logo, pointing to a JPG, PNG, WEBP, or SVG image (max 2048 chars). The platform downloads and hosts the image; see Logo and Icon Import. |
icon_url | string | null | Public URL of the store icon/favicon, pointing to a JPG, PNG, WEBP, or SVG image (max 2048 chars). The platform downloads and hosts the image; see Logo and Icon Import. |
disable_selling | boolean | false | When true, disables all e-commerce functionality on the store (checkout, paid plans, paid content). Selling is enabled by default. |
landing_enabled | boolean | false | When true, the public landing page is shown. Disabled by default for new aggregates. |
final_domain supports two formats:
- Platform subdomain:
mybookstore.publica.la(accessible athttps://mybookstore.publica.la) - Custom domain:
www.mybookstore.com(requires DNS configuration pointing to Publica.la)
If omitted, a domain is auto-generated from the store name (e.g., name "My Bookstore" generates mybookstore.publica.la).
Logo and Icon Import
When you send logo_url or icon_url, the platform checks the URL during validation, then downloads the image and stores it as the store's own logo or icon:
- The URL must be reachable when you make the request and must serve a JPG, PNG, WEBP, or SVG image, as reported by the
Content-Typeheader the server returns. - The image can be at most 20,000 KB.
- Responses return the URL of the hosted copy on the platform's asset domain, not the URL you sent. Treat it as an opaque value.
- The icon sizes derived from
icon_url(favicon and app icons) are generated in the background, so they can take a moment to appear on the store.
Auth Token (SSO) Fields
Configure single sign-on for the sub-tenant by sending an external_auth object. This is the same JWT (HS256) auth token integration that previously required a manual setup through support; you can now provision it programmatically for any of your sub-tenants. See the Auth Token guide for the token format and login flow.
| Field | Type | Default | Description |
|---|---|---|---|
external_auth.key | string | null | Write-only. The HMAC secret used to sign your JWTs (minimum 32 characters). Sending a valid key enables SSO. Never returned in any response. |
external_auth.issuer | string | null | Your platform identifier. Must match the iss claim in your tokens. Required when key is sent. |
external_auth.redirect_url | string | null | Where users are sent on auth errors. Required when key is sent. |
external_auth.login_url | string | null | Your platform's login URL. |
external_auth.logout_url | string | null | Where users are sent after logging out. |
external_auth.home_url | string | null | Your platform's home URL. |
external_auth.account_url | string | null | Your platform's account URL. |
The key is a shared secret and is never returned by any endpoint (create, get, list, or update). Responses include an external_auth object with every field except key, plus an enabled boolean that reflects whether a key is configured.
Storefront Redirect Fields
Send a storefront_redirect object to send visitors of the sub-tenant's storefront to an external site. Use it for stores that only need to serve reading and leave browsing to another site, such as a discovery portal you run.
| Field | Type | Default | Description |
|---|---|---|---|
storefront_redirect.enabled | boolean | false | Turns the redirect on (true) or off (false). Required when storefront_redirect is sent. |
storefront_redirect.url | string | null | Where visitors are sent. Must be an http or https URL (max 2048 chars). Required when enabled is true. |
While the redirect is on, every storefront page sends visitors to url, on every domain of the store and whether or not they are signed in. The reader and the store dashboard keep working. When a reader signs in through an integration that sends reader_exit_url (see the Auth Token guide), that URL takes precedence over url for that reader.
Inherited from Aggregator
These values are automatically set from the aggregator and cannot be overridden:
| Attribute | Description |
|---|---|
| Fees | Transaction and sales fee structure |
| Currencies | Main and secondary currency configuration |
What Happens on Create
When you create an aggregate, the platform automatically:
- Creates the tenant with the provided configuration
- Provisions store infrastructure (library, default shelves, sales settings)
- Inherits fees and currencies from the aggregator
- Creates an admin user linked to the email of the authenticated user
- Returns an
api_keyto authenticate requests on behalf of the new store
Request Examples
Minimal (Auto-Generated Domain)
curl -X POST "https://yourstore.publica.la/api/v3/aggregates" \
-H "X-User-Token: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "My Bookstore",
"lang": "en",
"timezone": "America/New_York",
"support_email": "[email protected]"
}'
The domain is auto-generated as mybookstore.publica.la.
With Auth Token (SSO)
curl -X POST "https://yourstore.publica.la/api/v3/aggregates" \
-H "X-User-Token: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "My Bookstore",
"lang": "en",
"timezone": "America/New_York",
"support_email": "[email protected]",
"external_auth": {
"key": "your-32-character-or-longer-secret-key",
"issuer": "my-platform",
"redirect_url": "https://my-platform.com/auth-error",
"logout_url": "https://my-platform.com/logout"
}
}'
With Flashcards and Storefront Redirect
curl -X POST "https://yourstore.publica.la/api/v3/aggregates" \
-H "X-User-Token: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "University Library",
"lang": "en",
"timezone": "America/New_York",
"support_email": "[email protected]",
"reader_flashcards_enabled": true,
"storefront_redirect": {
"enabled": true,
"url": "https://discovery.example.com"
}
}'
With Branding and Custom Domain
curl -X POST "https://yourstore.publica.la/api/v3/aggregates" \
-H "X-User-Token: your-api-token" \
-H "Content-Type: application/json" \
-d '{
"name": "My Bookstore",
"final_domain": "www.mybookstore.com",
"external_id": "crm-12345",
"lang": "en",
"timezone": "America/New_York",
"support_email": "[email protected]",
"primary_color": "#1A237E",
"secondary_color": "#FF6F00",
"reader_accent_color": "#0066CC",
"logo_url": "https://example.com/assets/logo.png",
"icon_url": "https://example.com/assets/icon.png"
}'
Response
Success Response (201 Created)
{
"data": {
"id": "42",
"external_id": null,
"slug": "my-bookstore",
"name": "My Bookstore",
"final_domain": "mybookstore.publica.la",
"lang": "en",
"timezone": "America/New_York",
"api_key": "api-a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"primary_color": null,
"secondary_color": null,
"reader_accent_color": null,
"reader_flashcards_enabled": false,
"logo_url": null,
"icon_url": null,
"disable_selling": false,
"landing_enabled": false,
"storefront_redirect": {
"enabled": false,
"url": null
},
"external_auth": {
"enabled": false,
"issuer": null,
"redirect_url": null,
"login_url": null,
"logout_url": null,
"home_url": null,
"account_url": null
},
"created_at": "2026-03-24T15:30:00+00:00"
}
}
The api_key is always included in the create response. To retrieve it later, use ?include=api_key on the get or list endpoints.
Use the api_key in the X-User-Token header to make API requests directly to the sub-tenant's endpoints (content, orders, etc.).
Error Handling
Validation Errors (422)
{
"message": "The given data was invalid.",
"errors": {
"name": ["The name field is required."],
"final_domain": ["The final domain has already been taken."],
"support_email": ["The support email must be a valid email address."]
}
}
Invalid Domain Format (422)
{
"message": "The given data was invalid.",
"errors": {
"final_domain": ["The final_domain format is invalid."]
}
}
Invalid Color Format (422)
{
"message": "The given data was invalid.",
"errors": {
"primary_color": ["The primary color format is invalid."]
}
}
Invalid Logo or Icon (422)
Returned when the image cannot be fetched, is not a supported format, or is larger than 20,000 KB:
{
"message": "The given data was invalid.",
"errors": {
"logo_url": ["The logo url could not be downloaded."],
"icon_url": ["The icon url must point to a JPG, PNG, WEBP or SVG image."]
}
}
Authorization Errors (403)
Returned when the authenticated tenant is not allowed to provision sub-tenants. Two messages are possible:
{
"message": "Tenant does not have aggregation capabilities."
}
{
"message": "Aggregated tenants cannot provision tenants."
}
Authentication Errors (401)
{
"message": "Unauthenticated."
}
Validation Rules
| Field | Rules |
|---|---|
name | Required, string, max 255 characters |
final_domain | Optional, string, max 255 characters, unique, valid domain format |
external_id | Optional, nullable, string, max 255 characters, unique per aggregator |
lang | Required, string, one of the supported language codes |
timezone | Required, valid IANA timezone |
support_email | Required, valid email, max 255 characters |
primary_color | Optional, nullable, hex color format (#RRGGBB) |
secondary_color | Optional, nullable, hex color format (#RRGGBB) |
reader_accent_color | Optional, nullable, hex color format (#RRGGBB) |
reader_flashcards_enabled | Optional, boolean |
logo_url | Optional, nullable, http or https URL, max 2048 characters. Must be reachable and serve a JPG, PNG, WEBP, or SVG image of at most 20,000 KB |
icon_url | Optional, nullable, http or https URL, max 2048 characters. Must be reachable and serve a JPG, PNG, WEBP, or SVG image of at most 20,000 KB |
disable_selling | Optional, boolean |
landing_enabled | Optional, boolean |
storefront_redirect | Optional, object |
storefront_redirect.enabled | Required when storefront_redirect is sent, boolean |
storefront_redirect.url | Required when storefront_redirect.enabled is true, nullable, http or https URL, max 2048 characters |
external_auth.key | Required when external_auth is sent, string, minimum 32 characters |
external_auth.issuer | Required when external_auth is sent, string, max 255 characters |
external_auth.redirect_url | Required when external_auth is sent, valid URL, max 2048 characters |
external_auth.login_url | Optional, nullable, valid URL, max 2048 characters |
external_auth.logout_url | Optional, nullable, valid URL, max 2048 characters |
external_auth.home_url | Optional, nullable, valid URL, max 2048 characters |
external_auth.account_url | Optional, nullable, valid URL, max 2048 characters |
Validation Error (422)
{
"message": "The given data was invalid.",
"errors": {
"external_auth.key": [
"The external authentication key must be at least 256 bits (32 bytes)."
]
}
}
See Also
- List Aggregates - Query your aggregates
- Get Aggregate - Retrieve aggregate details
- Update Aggregate - Modify configuration
- Overview - API overview