Objects API
The Objects API provides comprehensive search and management capabilities for objects within registers and schemas.
Endpoints
Search Objects
GET /api/objects/{register}/{schema}
Retrieves a paginated list of objects that match the specified register and schema.
Parameters
Standard Filters
- Any object field: Filter by any field in the object schema (e.g.,
name,status,type, etc.)
Metadata Filters
- register: Filter by register identifier
- schema: Filter by schema identifier
- uuid: Filter by object UUID
- organisation: Filter by organisation UUID
- owner: Filter by object owner
- application: Filter by application
- created: Filter by creation date
- updated: Filter by last update date
- published: Filter by publication date
- depublished: Filter by depublication date
- deleted: Filter by deletion date
Pagination
_limit: Number of items per page (default: 20)_offset: Number of items to skip_page: Current page number (alternative to_offset)
Search
_search: Full-text search term
Rendering
_extend: Properties to extend (comma-separated or array)- Extend object properties by fetching related objects
- Special values:
_registers: Include full register object(s) in@self.registers_schemas: Include full schema object(s) in@self.schemas_files(or canonical@self.files): Include full file metadata in@self.files. Equivalent spellings, normalized internally. Default behavior: when this extension is NOT requested,@self.filesis a lightweight list of integer file IDs ([123, 456]). When requested,@self.filesis an array of full file objects withid,path,title,accessUrl,downloadUrl,type,extension,size,hash,published,modified,labels. The lightweight default uses a single batched query regardless of page size.
- When any
_extendis requested,@self.objectswill contain all extended objects indexed by UUID - Performance note: Using
_extendadds approximately 300ms overhead per request due to additional database queries for related objects. For performance-critical applications, consider fetching related objects separately or using caching strategies. - Heavily discouraged on list endpoints:
_extend[]=@self.files(or_files). This extension causes one file lookup and one tag lookup per row on the page (N+1 queries scaling with page size) and will result in degraded performance. Use it only when full file metadata is genuinely required for every row of the list. For single-object reads (show endpoints), the cost is fixed (one extra request) and acceptable. For lists, prefer the lightweight default and look up full metadata for specific objects on demand.
_fields: Fields to include (comma-separated)_filter: Fields to filter (comma-separated)_unset: Fields to exclude (comma-separated)
Faceting (SOLR only)
_facets: Facet configuration object_facetable: Enable facetable field discovery (boolean)
Aggregations (SOLR only)
_aggregations: Enable aggregations in response (boolean)- Currently an alias for facets, but reserved for future advanced aggregation features
- Future capabilities: Statistical aggregations (sum, avg, min, max, count), time series analysis, nested aggregations, and complex mathematical analysis using SOLR's Analytics Component
Debug (SOLR only)
_debug: Enable debug information in response (boolean)- Includes SOLR query details, execution times, and internal processing information
Source Selection
_source: Force search sourcedatabase: Use database search (default for simple queries)indexorsolr: Use SOLR search engine
Sorting
_order: Sort specification (field:direction or array of field:direction pairs)
Example Requests
# Basic search
GET /api/objects/1/3?_limit=10
# Search with property extension
GET /api/objects/1/3?_extend=aanbieder&_limit=10
# Search with register and schema metadata
GET /api/objects/1/3?_extend=_registers,_schemas&_limit=10
# Combine property extension with metadata
GET /api/objects/1/3?_extend=aanbieder,_registers,_schemas&_limit=10
# Search with facets (SOLR only)
GET /api/objects/1/3?_facetable=true&_limit=10
# Search with aggregations (SOLR only)
GET /api/objects/1/3?_aggregations=true&_limit=10
# Search with debug information (SOLR only)
GET /api/objects/1/3?_debug=true&_limit=10
# Force database search
GET /api/objects/1/3?_source=database&_limit=10
# Force SOLR search
GET /api/objects/1/3?_source=index&_limit=10
# Search with filtering
GET /api/objects/1/3?naam=test&_limit=10
# Search with date filtering
GET /api/objects/1/3?created=2024-01-01&_limit=10
Response Format
{
"results": [
{
"id": "uuid",
"naam": "Object Name",
"beschrijvingKort": "Short description",
"@self": {
"id": "uuid",
"name": "Object Name",
"register": "1",
"schema": "3",
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z",
"owner": "admin",
"organisation": "org-uuid"
}
}
],
"total": 1000,
"page": 1,
"pages": 50,
"limit": 20,
"@self": {
"source": "database",
"registers": {
"1": { "id": 1, "title": "Register Name", "..." : "..." }
},
"schemas": {
"3": { "id": 3, "title": "Schema Name", "..." : "..." }
},
"objects": {
"related-uuid": { "..." : "..." }
}
},
"facets": {
"facet_queries": [],
"facet_fields": {
"self_register": ["1", 1000],
"self_schema": ["3", 1000]
},
"facet_ranges": [],
"facet_intervals": [],
"facet_heatmaps": []
},
"aggregations": {
"facet_queries": [],
"facet_fields": {
"self_register": ["1", 1000],
"self_schema": ["3", 1000]
},
"facet_ranges": [],
"facet_intervals": [],
"facet_heatmaps": []
},
"debug": {
"url": "http://solr:8983/solr/openregister/select?...",
"solr_numFound": 1000,
"solr_status": 0,
"translated_query": {...},
"solr_facets": {...}
}
}
Note: The @self section at the response root level contains:
registers: Only included when_extend=_registersis specifiedschemas: Only included when_extend=_schemasis specifiedobjects: Extended/related objects indexed by UUID (included when any_extendis specified)
Response Fields
results: Array of matching objectstotal: Total number of matching objectspage: Current page numberpages: Total number of pageslimit: Number of items per pagefacets: Facet data (only when_facetable=true)aggregations: Aggregation data (only when_aggregations=true)debug: Debug information (only when_debug=true)_source: Search source used (databaseorindex)@self.ignoredFilters: Array of filter property names that were ignored (only present when filters are ignored)
Ignored Filters
When you filter by a property that doesn't exist in the schema, the API will:
- Return zero results for that schema (to prevent unfiltered data leakage)
- Include the ignored property names in
@self.ignoredFilters
This is particularly useful for:
- Debugging: Understanding why a query returns unexpected (empty) results
- Multi-schema searches: When filtering by a property that exists in some schemas but not others
Example Request with Invalid Filter:
GET /api/objects/1/3?invalidProperty=test&_limit=10
Response:
{
"results": [],
"total": 0,
"page": 1,
"pages": 0,
"limit": 10,
"@self": {
"ignoredFilters": ["invalidProperty"],
"source": "database"
}
}
Note: In multi-schema searches, a filter is reported as ignored if it doesn't exist in at least one of the searched schemas. This helps identify when a filter only applies to a subset of schemas.
Common causes for ignored filters:
- Typos in property names (e.g.,
nameinstead ofnaam) - Using pagination parameters without underscore prefix (use
_limitnotlimit) - Filtering by a property from a different schema
Search All Objects
GET /api/objects
Retrieves a paginated list of objects across all registers and schemas that the current user has access to.
Parameters
Same as the register/schema specific endpoint, with additional filtering options:
Multi-Register/Schema Search
You can filter by specific registers and schemas using query parameters:
- 'register': Single register ID or array of register IDs
- 'schema': Single schema ID or array of schema IDs
Examples:
# Search in single register and schema
GET /api/objects?register=1&schema=3
# Search in multiple registers
GET /api/objects?register[]=1®ister[]=2&schema=3
# Search in multiple schemas
GET /api/objects?register=1&schema[]=3&schema[]=4
# Search across multiple registers and schemas
GET /api/objects?register[]=1®ister[]=2&schema[]=3&schema[]=4&schema[]=5
# Combine with other filters
GET /api/objects?register[]=1®ister[]=2&schema[]=3&_search=test&_limit=50
Use Cases:
- Search across all publication types (articles, books, reports) at once
- Query multiple data sources simultaneously
- Build unified views across related schemas
- Compare data across different registers
Note: The backend automatically uses SQL 'IN' clauses for efficient filtering when arrays are provided.
Get Single Object
GET /api/objects/{register}/{schema}/{id}
Retrieves a single object by ID.
Parameters
_extend: Properties to extend (comma-separated or array)- Extend object properties by fetching related objects
- Special values:
_registers: Include full register object in@self.registers_schemas: Include full schema object in@self.schemas_files(or canonical@self.files): Include full file metadata in@self.files. Equivalent spellings, normalized internally. Without this extension,@self.filesis a lightweight list of integer file IDs.
- When any
_extendis requested,@self.objectswill contain all extended objects indexed by UUID
_fields: Fields to include_filter: Fields to filter_unset: Fields to exclude
Example Requests
# Get single object
GET /api/objects/1/3/uuid
# Get single object with extended property
GET /api/objects/1/3/uuid?_extend=aanbieder
# Get single object with register and schema metadata
GET /api/objects/1/3/uuid?_extend=_registers,_schemas
# Get single object with full file metadata (both spellings work)
GET /api/objects/1/3/uuid?_extend=@self.files
GET /api/objects/1/3/uuid?_extend=_files
# Combine property extension with metadata
GET /api/objects/1/3/uuid?_extend=aanbieder,_registers,_schemas
Single Object Response Format
{
"id": "uuid",
"naam": "Object Name",
"beschrijvingKort": "Short description",
"aanbieder": "related-uuid",
"@self": {
"id": "uuid",
"name": "Object Name",
"register": "1",
"schema": "3",
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z",
"owner": "admin",
"organisation": "org-uuid",
"registers": {
"1": { "id": 1, "title": "Register Name", "..." : "..." }
},
"schemas": {
"3": { "id": 3, "title": "Schema Name", "..." : "..." }
},
"objects": {
"related-uuid": { "..." : "..." }
}
}
}
Note: For single objects, the registers, schemas, and objects are included in the object's own @self section (not at the response root level).
Create Object
POST /api/objects/{register}/{schema}
Creates a new object in the specified register and schema.
Authentication: Public (@PublicPage) — no authentication required.
Request Formats
JSON
curl -X POST /api/objects/1/3 \
-H "Content-Type: application/json" \
-d '{
"naam": "New Object",
"beschrijvingKort": "A short description",
"type": "Leverancier"
}'
Multipart Form Data (with file uploads)
Use multipart/form-data to upload files alongside object data. Each form field maps to a schema property. Files are automatically stored in Nextcloud and linked to the object.
curl -X POST /api/objects/1/3 \
-F "naam=New Object" \
-F "type=Leverancier" \
-F "logo=@/path/to/logo.png" \
-F 'contactpersonen=[{"voornaam":"John","achternaam":"Doe"}]'
Important notes for multipart requests:
- Each form field name must match a schema property name
- File fields (like
logo) should contain a file upload — the backend will store the file and set the property value - Complex fields (arrays, objects) must be JSON-stringified (e.g.,
contactpersonen=[{"voornaam":"John"}]) - There is no file size limit imposed by the API (only PHP/server limits apply)
Response
{
"id": "uuid",
"naam": "New Object",
"type": "Leverancier",
"logo": "data:image/png;base64,...",
"@self": {
"id": "uuid",
"register": "1",
"schema": "3",
"files": [
{
"id": "806",
"path": "files/Open Registers/.../logo_1234567890_abc123.png",
"title": "logo_1234567890_abc123.png",
"accessUrl": "http://localhost:8080/index.php/s/shareToken",
"downloadUrl": "http://localhost:8080/index.php/s/shareToken/download",
"type": "image/png",
"size": 12345,
"labels": ["property:logo"]
}
],
"created": "2024-01-01T00:00:00Z"
}
}
Status: 201 Created
Update Object (Full Replace)
PUT /api/objects/{register}/{schema}/{id}
Replaces all fields of an existing object. Fields not included in the request body will be set to null.
Authentication: Required (user must be logged in).
Note: PUT does not support multipart file uploads due to a PHP limitation (
$_FILESis only populated for POST requests). Use JSON for PUT requests, or use the POST-as-PATCH endpoint below for file uploads on existing objects.
curl -X PUT /api/objects/1/3/uuid \
-u admin:admin \
-H "Content-Type: application/json" \
-d '{
"naam": "Updated Object",
"beschrijvingKort": "Updated description",
"type": "Gemeente"
}'
Status: 200 OK
Patch Object (Partial Update)
PATCH /api/objects/{register}/{schema}/{id}
Partially updates an existing object. Only the fields included in the request body are updated; other fields remain unchanged.
Authentication: Required (user must be logged in).
Note: PATCH does not support multipart file uploads due to the same PHP limitation as PUT. Use the POST-as-PATCH endpoint below for file uploads on existing objects.
curl -X PATCH /api/objects/1/3/uuid \
-u admin:admin \
-H "Content-Type: application/json" \
-d '{"naam": "Patched Name"}'
Status: 200 OK
Update Object with File Upload (POST-as-PATCH)
POST /api/objects/{register}/{schema}/{id}
Partially updates an existing object using PATCH semantics, with support for multipart file uploads. This endpoint exists because PHP only populates $_FILES for POST requests — making it impossible to upload files via PUT or PATCH with multipart/form-data.
Authentication: Public (@PublicPage) — no authentication required.
When to Use
Use this endpoint instead of PUT/PATCH when you need to:
- Upload or replace files on an existing object
- Update an object from a public (unauthenticated) context with file attachments
- Submit form data with file inputs to an existing object
Example Requests
# Update name and upload a new logo (no authentication needed)
curl -X POST /api/objects/1/3/uuid \
-F "naam=Updated Name" \
-F "logo=@/path/to/new-logo.png"
# Update only the logo file
curl -X POST /api/objects/1/3/uuid \
-F "logo=@/path/to/new-logo.png"
# With authentication
curl -X POST /api/objects/1/3/uuid \
-u admin:admin \
-F "naam=Updated Name" \
-F "logo=@/path/to/new-logo.png"
Behavior
- Fields included in the request are merged with existing object data (PATCH semantics)
- Fields not included in the request remain unchanged
- Uploaded files replace any previously stored file for that property
- The object must already exist; returns
404if not found
Status: 200 OK
Delete Object
DELETE /api/objects/{register}/{schema}/{id}
Deletes an existing object.
Authentication: Required (user must be logged in).
curl -X DELETE /api/objects/1/3/uuid -u admin:admin
Status: 200 OK
File Uploads
How File Uploads Work
When a file is uploaded via multipart/form-data (on POST create or POST-as-PATCH), the following happens:
- The file is extracted from
$_FILESby the controller - The file is converted to a base64 data URI internally
- The file property handler stores the file in Nextcloud's file system under the object's folder
- A public share link is created for the file
- The file metadata is added to
@self.fileswith aproperty:{fieldName}label
File Storage
Files are stored in the Nextcloud file system under:
files/Open Registers/{Register Name}/{object-uuid}/{fieldName}_{timestamp}_{hash}.{ext}
For unauthenticated (public) requests, files are stored under the OpenRegister system user account. For authenticated requests, files are stored under the requesting user's account.
Accessing Files
Each stored file has:
accessUrl: A public Nextcloud share link to view the filedownloadUrl: A direct download link (append/downloadto the share link)
Both URLs are publicly accessible without authentication.
Endpoint Comparison for File Support
| Method | URL | Auth Required | File Uploads | Use Case |
|---|---|---|---|---|
POST | /api/objects/{register}/{schema} | No | Yes | Create with files |
POST | /api/objects/{register}/{schema}/{id} | No | Yes | Update with files |
PUT | /api/objects/{register}/{schema}/{id} | Yes | No | Full replace (JSON only) |
PATCH | /api/objects/{register}/{schema}/{id} | Yes | No | Partial update (JSON only) |
Why can't PUT/PATCH handle file uploads? PHP only populates the
$_FILESsuperglobal for POST requests. When using PUT or PATCH withmultipart/form-data, the request body is not parsed into$_FILESby PHP, so uploaded files cannot be accessed. The POST-as-PATCH endpoint works around this limitation.
Error Handling
SOLR-Only Features in Database Mode
When using _source=database (or default database mode), certain features are not available:
_facetable=true: Will return an error_aggregations=true: Will return an error
Error Response:
{
"error": "Facets and aggregations are only available when using SOLR search engine. Please use _source=index parameter to enable SOLR search, or remove _facetable/_aggregations parameters."
}
Performance Considerations
- Database mode: Faster for simple queries, supports all basic filtering
- SOLR mode: Better for complex searches, faceting, and aggregations
- Automatic selection: System automatically chooses the best source unless
_sourceis specified
@self Metadata
Objects include a special @self metadata section that contains system-managed information:
{
"@self": {
"id": "object-uuid",
"name": "Object Name",
"register": "1",
"schema": "3",
"created": "2024-01-01T00:00:00Z",
"updated": "2024-01-01T00:00:00Z",
"owner": "owner-uuid",
"organisation": "org-uuid",
"published": "2024-01-01T00:00:00Z",
"depublished": null
}
}
Modifiable @self Properties
When creating or updating objects, you can explicitly set certain @self metadata properties:
owner: Object owner UUIDorganisation: Organization UUIDpublished: Publication timestampdepublished: Depublication timestamp
Example:
{
"naam": "My Object",
"@self": {
"owner": "user-uuid",
"organisation": "org-uuid",
"published": "2024-01-01T00:00:00Z"
}
}
For detailed information about @self metadata handling, see Self Metadata Handling.
Security
- RBAC: Respects role-based access control
- Multitenancy: Filters results by user's organisation when enabled
- Admin override: Admin users can bypass RBAC and multitenancy restrictions
- Published objects: Objects that are currently published (published date ≤ now AND depublished date is null or > now) are publicly available and bypass both RBAC and multitenancy restrictions, making them visible to all users regardless of their roles or organization