Skip to main content

Entities API

REST API for managing custom entities and their records.

All endpoints on this page are relative to the base URL: https://api-eu1.joryio.com - see API Overview.

Overview

The Entities API allows you to programmatically:

  • Create and manage entity definitions (schemas)
  • Add, update, and delete entity records (data)
  • Query and search records
  • Manage entity fields and relationships

Authentication

All endpoints require authentication via Bearer token:

Authorization: Bearer YOUR_API_KEY

And workspace context via header:

X-Workspace-Id: YOUR_WORKSPACE_ID

Entity Definitions

List All Entities

Get all entity definitions in the current workspace.

GET /entities

Response:

[
{
"id": "uuid",
"organizationId": "uuid",
"workspaceId": "uuid",
"name": "products",
"displayName": "Products",
"description": "Product catalog",
"collectionName": "entity_products",
"primaryKey": "_id",
"displayField": "name",
"settings": {
"allowDuplicates": false,
"enableAudit": true,
"enableVersioning": false,
"softDelete": true
},
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
]

Get Entity by ID

Retrieve a specific entity definition.

GET /entities/:entityId

Response:

{
"id": "uuid",
"name": "products",
"displayName": "Products",
...
}

Create Entity

Create a new entity definition with fields.

POST /entities

Request Body:

{
"name": "products",
"displayName": "Products",
"description": "Product catalog with pricing",
"displayField": "name",
"fields": [
{
"name": "sku",
"displayName": "SKU",
"fieldType": "string",
"validation": {
"required": true,
"unique": true,
"minLength": 5,
"maxLength": 20
},
"indexed": true,
"displayOrder": 0
},
{
"name": "name",
"displayName": "Product Name",
"fieldType": "string",
"validation": {
"required": true,
"maxLength": 255
},
"displayOrder": 1
},
{
"name": "price",
"displayName": "Price",
"fieldType": "currency",
"validation": {
"required": true,
"min": 0
},
"displayOrder": 2
},
{
"name": "description",
"displayName": "Description",
"fieldType": "markdown",
"displayOrder": 3
},
{
"name": "active",
"displayName": "Active",
"fieldType": "boolean",
"validation": {
"default": true
},
"displayOrder": 4
}
],
"settings": {
"allowDuplicates": false,
"enableAudit": true,
"enableVersioning": false,
"softDelete": true
}
}

Field Types:

  • string, email, phone, url
  • number, integer, currency, percentage
  • boolean
  • date, datetime
  • markdown, html, json
  • image_url, file_url

Response:

{
"id": "uuid",
"name": "products",
...
}

Update Entity

Update entity definition (name, description, settings).

PUT /entities/:entityId

Request Body:

{
"displayName": "Updated Products",
"description": "Updated description",
"displayField": "sku",
"settings": {
"enableAudit": false
}
}

Delete Entity

Delete an entity and all its records.

DELETE /entities/:entityId

Response: 204 No Content

Warning: This permanently deletes the entity schema and all records!

Entity Fields

List Fields

Get all fields for an entity.

GET /entities/:entityId/fields

Response:

[
{
"id": "uuid",
"entityDefinitionId": "uuid",
"name": "sku",
"displayName": "SKU",
"description": "Product SKU",
"fieldType": "string",
"validation": {
"required": true,
"unique": true,
"minLength": 5,
"maxLength": 20
},
"indexed": true,
"indexType": "btree",
"displayOrder": 0,
"hidden": false,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
]

Add Field

Add a new field to an entity.

POST /entities/:entityId/fields

Request Body:

{
"name": "category",
"displayName": "Category",
"fieldType": "string",
"validation": {
"required": false
},
"indexed": true,
"displayOrder": 5
}

Delete Field

Remove a field from an entity.

DELETE /entities/:entityId/fields/:fieldId

Response: 204 No Content

Warning: This removes the field definition. Existing record data is not deleted but becomes inaccessible.

Entity Records

List Records

Retrieve records from an entity with optional filtering.

GET /entities/:entityId/records

Query Parameters:

  • filter - JSON filter object (URL encoded)
  • sort - JSON sort object (URL encoded)
  • limit - Max records to return (default: 50)
  • offset - Number of records to skip (default: 0)

Examples:

# All active products
GET /entities/ENTITY_ID/records?filter={"active":true}

# Products sorted by price descending
GET /entities/ENTITY_ID/records?sort={"price":-1}

# Paginated results
GET /entities/ENTITY_ID/records?limit=20&offset=40

Response:

{
"data": [
{
"_id": "665f1c0a9b2e4d0012ab34cd",
"sku": "PROD-001",
"name": "Widget",
"price": 29.99,
"description": "A great widget",
"active": true,
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
],
"total": 150
}

Get Record by ID

Retrieve a specific record.

GET /entities/:entityId/records/:recordId

Response:

{
"_id": "665f1c0a9b2e4d0012ab34cd",
"sku": "PROD-001",
"name": "Widget",
...
}

Create Record

Add a new record to an entity.

POST /entities/:entityId/records

Request Body:

{
"sku": "PROD-001",
"name": "Widget",
"price": 29.99,
"description": "A great widget",
"active": true
}

Validation:

  • Required fields must be present
  • Unique fields must be unique
  • Values must match field types
  • Min/max constraints are enforced

Response:

{
"_id": "665f1c0a9b2e4d0012ab34cd",
"sku": "PROD-001",
...
}

Update Record

Update an existing record.

PUT /entities/:entityId/records/:recordId

Request Body:

{
"price": 34.99,
"description": "An even better widget"
}

Partial updates are supported - only include fields you want to change.

Delete Record

Remove a record from an entity.

DELETE /entities/:entityId/records/:recordId

Response: 204 No Content

If soft delete is enabled, the record is marked as deleted. Otherwise, it's permanently removed.

Bulk Create Records (array body)

There is no separate bulk endpoint: POST /entities/:entityId/records accepts either a single record object or a bare JSON array of record objects (no wrapper object). The array form creates up to 1000 records in one request.

POST /entities/:entityId/records

Query Parameters:

  • triggerAlerts - Set to true to fire relationship alerts (restock and similar) for records that transition in this import (default: false)

Request Body:

A JSON array (max 1000 elements; an empty array or more than 1000 returns 400). Each element uses the same data envelope as the single-object form.

Each element's envelope is validated individually: an element without a data object is reported in failed by its array index - it is never silently accepted - and the remaining valid elements are still inserted. Field-definition validation (required fields, types, min/max) applies to the whole batch, exactly as for single creates.

[
{
"data": {
"sku": "PROD-001",
"name": "Widget A",
"price": 29.99
}
},
{
"data": {
"sku": "PROD-002",
"name": "Widget B",
"price": 39.99
}
}
]

Response:

Unlike the object form (which returns the created record), the array form returns an aggregate summary:

{
"processed": 2,
"inserted": 2,
"insertedIds": ["id-1", "id-2"],
"failed": []
}
FieldDescription
processedNumber of elements received in the request array
insertedRecords actually created
insertedIdsIDs of the created records, in insertion order
failedPer-element envelope failures: index (position in the request array), reason

Search Records

Full-text search across specified fields.

GET /entities/:entityId/search

Query Parameters:

  • q - Search query (required)
  • fields - Comma-separated field names to search (optional)
  • limit - Max results (default: 10)

Examples:

# Search across all indexed fields
GET /entities/ENTITY_ID/search?q=widget

# Search specific fields
GET /entities/ENTITY_ID/search?q=PROD-001&fields=sku,name

# Limit results
GET /entities/ENTITY_ID/search?q=widget&limit=5

Response:

[
{
"_id": "id",
"sku": "PROD-001",
"name": "Widget",
...
}
]

Advanced Queries

Aggregation

Perform aggregations on entity records.

POST /entities/:entityId/aggregate

Request Body:

{
"groupBy": "category",
"aggregations": [
{
"field": "price",
"operation": "avg",
"as": "avgPrice"
},
{
"field": "price",
"operation": "sum",
"as": "totalValue"
},
{
"operation": "count",
"as": "productCount"
}
]
}

Operations:

  • count - Count records
  • sum - Sum values
  • avg - Average values
  • min - Minimum value
  • max - Maximum value

Response:

{
"results": [
{
"category": "Electronics",
"avgPrice": 45.99,
"totalValue": 2299.50,
"productCount": 50
},
{
"category": "Clothing",
"avgPrice": 29.99,
"totalValue": 1499.50,
"productCount": 50
}
]
}

Complex Queries

Execute complex queries with MongoDB-style syntax.

POST /entities/query

Request Body:

{
"entity": "products",
"pipeline": [
{
"$match": {
"price": { "$gte": 20, "$lte": 50 },
"active": true
}
},
{
"$group": {
"_id": "$category",
"count": { "$sum": 1 },
"avgPrice": { "$avg": "$price" }
}
},
{
"$sort": { "avgPrice": -1 }
}
]
}

Filter Operators

When using filters, you can use MongoDB query operators:

Comparison

  • $eq - Equal to
  • $ne - Not equal to
  • $gt - Greater than
  • $gte - Greater than or equal
  • $lt - Less than
  • $lte - Less than or equal
  • $in - Value in array
  • $nin - Value not in array

Logical

  • $and - Logical AND
  • $or - Logical OR
  • $not - Logical NOT
  • $nor - Logical NOR

Element

  • $exists - Field exists
  • $type - Field type check

String

  • $regex - Regular expression match

Examples:

// Price between 10 and 100
{
"price": { "$gte": 10, "$lte": 100 }
}

// Active products in specific categories
{
"active": true,
"category": { "$in": ["Electronics", "Computers"] }
}

// Complex condition
{
"$or": [
{ "price": { "$lte": 20 } },
{ "onSale": true }
],
"active": true
}

Error Responses

400 Bad Request

{
"statusCode": 400,
"message": "Validation failed",
"errors": [
{
"field": "price",
"message": "price must be greater than or equal to 0"
}
]
}

401 Unauthorized

{
"statusCode": 401,
"message": "Unauthorized"
}

404 Not Found

{
"statusCode": 404,
"message": "Entity not found"
}

409 Conflict

{
"statusCode": 409,
"message": "Duplicate value for unique field 'sku'"
}

Rate Limits

The Entities API has no fixed per-endpoint rate limits today - see API Overview: Rate Limiting for the platform-wide behavior and how to handle 429 responses.

Best Practices

Performance

  1. Use indexes - Index fields you frequently filter/search by
  2. Paginate results - Don't fetch all records at once
  3. Limit field selection - Only fetch fields you need
  4. Cache responses - Cache frequently accessed data
  5. Batch operations - Use bulk endpoints when possible

Data Quality

  1. Validate before insert - Check data client-side
  2. Handle errors - Implement proper error handling
  3. Use transactions - For multi-record operations
  4. Clean regularly - Archive or delete old records

Security

  1. Never expose API keys - Keep them server-side
  2. Validate user input - Sanitize before sending to API
  3. Use HTTPS only - Never use HTTP
  4. Rotate keys regularly - Update API keys periodically
  5. Implement rate limiting - On your side too

Code Examples

JavaScript/Node.js

const axios = require('axios');

const api = axios.create({
baseURL: 'https://api-eu1.joryio.com',
headers: {
'Authorization': `Bearer ${process.env.HIPPO_API_KEY}`,
'X-Workspace-Id': process.env.WORKSPACE_ID
}
});

// Create entity
const entity = await api.post('/entities', {
name: 'products',
displayName: 'Products',
fields: [/* ... */]
});

// Create record
const record = await api.post(`/entities/${entity.data.id}/records`, {
sku: 'PROD-001',
name: 'Widget',
price: 29.99
});

// Search records
const results = await api.get(`/entities/${entity.data.id}/search`, {
params: { q: 'widget' }
});

Python

import requests
import os

api_key = os.getenv('HIPPO_API_KEY')
workspace_id = os.getenv('WORKSPACE_ID')

headers = {
'Authorization': f'Bearer {api_key}',
'X-Workspace-Id': workspace_id
}

# Create entity
response = requests.post(
'https://api-eu1.joryio.com/entities',
json={
'name': 'products',
'displayName': 'Products',
'fields': [...]
},
headers=headers
)
entity = response.json()

# Create record
response = requests.post(
f'https://api-eu1.joryio.com/entities/{entity["id"]}/records',
json={
'sku': 'PROD-001',
'name': 'Widget',
'price': 29.99
},
headers=headers
)