HTTP reference
Collections API
Collection routes are defined explicitly in routes/collections.php (not a Laravel Route::resource). Every route requires auth + verified. Most actions also use Spatie permission: middleware; bulk delete/restore/force-delete authorize in the FormRequest. Super-admin bypasses via Gate::before.
Authorization model
Collection schema and item CRUD are gated server-side with PermissionEnum (can-show-collections, can-create-collections, can-edit-collections, can-delete-collections, restore/force-delete). The React UI (useCan) and AI tools share the same names. Item read/write also applies CollectionPermissionEnforcer (role collection_permissions field ACL / item_filter). See Effective permissions.
Headless Public CMS API
For external websites and integrations, use /api/v1/collections... (not these session routes). Auth is anonymous public role or Bearer API keys, gated by the Collection access matrix. See Public CMS API and GraphQL. Runnable companion: externa-bruno (PublicApi + GraphQL). Session pack helpers also live under Bruno Admin/ (not public API).
Relevant permission names:
can-show-collectionscan-create-collectionscan-edit-collectionscan-delete-collectionscan-restore-collectionscan-force-delete-collections
Collections
| Method | Path | Route name | Permission / notes |
|---|---|---|---|
GET | /collections | collections.index | can-show-collections (search, sort, direction, trashed) |
POST | /collections | collections.store | can-create-collections |
POST | /collections/bulk | collections.bulk | FormRequest: delete → delete; restore → restore; force_delete → force-delete |
POST | /collections/packs/{pack} | collections.packs.apply | can-create-collections (collection pack; auto-create deps) |
GET | /collections/{collection} | collections.show | can-show-collections |
PUT | /collections/{collection} | collections.update | can-edit-collections (PUT only) |
DELETE | /collections/{collection} | collections.destroy | can-delete-collections (soft delete) |
POST | /collections/{collection}/restore | collections.restore | can-restore-collections |
DELETE | /collections/{collection}/force | collections.force-delete | can-force-delete-collections |
PUT | /collections/{collection}/singleton-content | collections.singleton-content | can-edit-collections |
Controller: ContentCollectionController. Bulk body: { action, ids } with action ∈ delete | restore | force_delete.
Fields
| Method | Path | Route name | Permission |
|---|---|---|---|
GET | /collections/{collection}/fields | collections.fields.index | can-show-collections |
POST | /collections/{collection}/fields | collections.fields.store | can-edit-collections |
POST | /collections/{collection}/field-packs/{pack} | collections.field-packs.apply | can-edit-collections |
PATCH | /collections/{collection}/fields/{field} | collections.fields.update | can-edit-collections |
DELETE | /collections/{collection}/fields/{field} | collections.fields.destroy | can-edit-collections |
POST | /collections/{collection}/fields/reorder | collections.fields.reorder | can-edit-collections |
POST | /collections/{collection}/fields/{field}/duplicate | collections.fields.duplicate | can-edit-collections |
POST | /collections/{collection}/fields/{field}/toggle-form-visibility | collections.fields.toggle-form-visibility | can-edit-collections |
POST | /collections/{collection}/fields/{field}/layout-width | collections.fields.update-layout-width | can-edit-collections |
PUT | /collections/{collection}/form-layout | collections.form-layout.update | can-edit-collections |
Controller: FieldController. Types: Field types. Packs: Field types — Field packs. Locales for labels: project content locales (Project settings).
Items
Helpers registered before {item} so new / options / export are not captured as IDs:
| Method | Path | Route name | Permission |
|---|---|---|---|
GET | /collections/{collection}/items/new | collections.items.new | can-create-collections |
GET | /collections/{collection}/items/options | collections.items.field-options | can-show-collections |
GET | /collections/{collection}/items/export | collections.items.export | can-show-collections (?format=csv|json) |
PUT | /collections/{collection}/list-columns | collections.items.list-columns.update | can-edit-collections |
POST | /collections/{collection}/items/{item}/restore | collections.items.restore | can-restore-collections |
DELETE | /collections/{collection}/items/{item}/force | collections.items.force-delete | can-force-delete-collections |
GET | /collections/{collection}/items | collections.items.index | can-show-collections |
POST | /collections/{collection}/items | collections.items.store | can-create-collections |
POST | /collections/{collection}/items/bulk | collections.items.bulk | FormRequest per action (same as collection bulk) |
GET | /collections/{collection}/items/{item} | collections.items.show | can-show-collections |
PUT | /collections/{collection}/items/{item} | collections.items.update | can-edit-collections (PUT only) |
DELETE | /collections/{collection}/items/{item} | collections.items.destroy | can-delete-collections |
Controller: ItemController. Data model: Collections data model. Product UX (filters, History, export): Collection items.
Item bulk also runs CollectionPermissionEnforcer::assertItemWritable for delete/force-delete (and skips restore). IDs from another collection are rejected.
Item revisions
| Method | Path | Route name | Permission |
|---|---|---|---|
GET | /collections/{collection}/items/{item}/revisions | collections.items.revisions.index | can-show-collections |
POST | /collections/{collection}/items/{item}/revisions/{revision}/restore | collections.items.revisions.restore | can-edit-collections |
GET | /collections/{collection}/items/{item}/preview-as-role | collections.items.preview-as-role | can-show-collections |
Snapshots are written on every CollectionItemValuesWriter::sync (create/update/restore). Soft-deleted items keep history. Preview-as-role is JSON (role_id or as_public=1) — see Collection items.
Imports via AI / webhook
Bulk import does not live on these REST routes. Use:
- Authenticated AI tools (
ImportCollectionCsv,ImportRemoteJson, …) under AI API - Token webhook
POST /ai/webhooks/collection-import
Import job status: GET /ai/import-jobs/{jobId} (requires can-use-ai and can-create-collections).
Authorization
Verified users without the matching can-*-collections permission receive 403. Pest: tests/Feature/Collections/CollectionAuthorizationTest.php, tests/Feature/Collections/BulkActionsTest.php.
Advanced list filters
Admin items index: Search title + Filters popover (AND multi-field) write this dialect into the URL. Same operators for Public API + GraphQL filter:
?filter[title]=hello— contains (LIKE), backward compatible?filter[status][_eq]=published?filter[status][_neq]=draft?filter[tags][_in]=a,b?filter[body][_null]=1/[_nnull]=1?filter[price][_gte]=10/[_lte]/[_gt]/[_lt]?filter[title][_contains]=hello
See also Public CMS API and GraphQL.