Skip to main content

Data Resources

Data Resources allow agents to expose their own domain data (contacts, records, prompts, etc.) to the Studio platform via generic CRUD endpoints. Studio renders any declared resource as a generic table with no per-agent customization required.

Overview​

Declare DataResource objects on your agent and pass them in Agent(data_resources=[...]). The SDK auto-generates FastAPI routes. Studio discovers them at registration time and renders generic CRUD views.

from supervaizer import Agent, DataResource, DataResourceField, Editable, FieldType

contacts_resource = DataResource(
name="contacts",
display_name="Contacts",
description="Tenant contact pool",
fields=[
DataResourceField(name="id", editable=Editable.NEVER, visible_on=["list", "detail"]),
DataResourceField(name="first_name", required=True),
DataResourceField(name="email", field_type=FieldType.EMAIL),
],
on_list=lambda: contacts_repo.list_all(),
on_get=lambda item_id: contacts_repo.get(item_id),
on_create=lambda data: contacts_repo.create(data),
on_update=lambda item_id, data: contacts_repo.update(item_id, data),
on_delete=lambda item_id: contacts_repo.delete(item_id),
on_import=lambda records: contacts_repo.bulk_import(records),
importable=True,
)

agent = Agent(
name="My Agent",
# ... other params ...
data_resources=[contacts_resource],
)

DataResource Parameters​

ParameterTypeDefaultDescription
namestrrequiredURL-safe identifier (e.g. "contacts")
display_namestrderived from nameHuman-readable label shown in Studio
descriptionstr""Short description shown in Studio
fieldslist[DataResourceField][]Field schema for Studio rendering
read_onlyboolFalseIf True, only list/get routes are generated
importableboolFalseIf True, a bulk import route is generated
on_listCallable[[], list[dict]]requiredReturns all records
on_getCallable[[str], dict | None]optionalReturns one record by ID
on_createCallable[[dict], dict]required if not read_onlyCreates a record; must return dict with id
on_updateCallable[[str, dict], dict]optionalUpdates a record by ID
on_deleteCallable[[str], bool]optionalDeletes a record; returns True on success
on_importCallable[[list[dict]], dict]required if importableBulk insert; required if importable=True

DataResourceField Parameters​

ParameterTypeDefaultDescription
namestrrequiredColumn name (e.g. "first_name")
field_typeFieldTypeFieldType.STRINGOne of: string, integer, boolean, date, datetime, text, email, url
labelstr | Nonederived (name.title())Human-readable label
requiredboolFalseRequired on create form
editableEditableEditable.ALWAYSControls when Studio may edit this field
visible_onlist[str]["list","detail","create","edit"]Views that render this field
descriptionstr | NoneNoneHelp text shown in Studio form
related_resourcestr | NoneNoneName of another DataResource this FK-references

Editable Values​

ValueMeaning
Editable.ALWAYSField appears on both create and edit forms
Editable.CREATE_ONLYSet on create; shown read-only on edit
Editable.NEVERAgent-controlled; never shown in a form input (e.g. auto-generated IDs)

Generated Routes​

For each declared operation, the SDK registers a route under the agent's path:

GET /supervaizer/agents/{slug}/data/{resource}/ list
GET /supervaizer/agents/{slug}/data/{resource}/{id} get
POST /supervaizer/agents/{slug}/data/{resource}/ create
PUT /supervaizer/agents/{slug}/data/{resource}/{id} update
DELETE /supervaizer/agents/{slug}/data/{resource}/{id} delete
POST /supervaizer/agents/{slug}/data/{resource}/import/ bulk import

All routes require the server API key (X-API-Key header).

Job and Case Metadata​

Agents can also attach arbitrary domain metadata to Jobs and Cases, which Studio renders in a dedicated panel on the detail view.

from supervaizer import Job, JobContext, Case

# In your job_start handler:
job = Job.new(
job_context=JobContext(workspace_id=workspace_id, job_id=job_id),
agent_name="my-agent",
metadata={
"campaign_id": "c-001",
"scenario": "hr_screening",
"phase": "interviews",
},
)

# In your case processing:
case = Case.start(
job_id=job_id,
account=account,
name=contact_name,
description="...",
metadata={
"contact_email": "[email protected]",
"language": "en",
},
)

The metadata dict is sent to Studio via the existing event system and stored on the Job/Case record. It is rendered as a read-only key/value panel — not editable from Studio.