Galadri

Data Model

Managed data that the agent creates automatically, plus the REST API for direct access.

How data works in Galadri

The agent automatically creates and maintains structured records as users interact. When someone says "I have a 2022 Honda Civic," the agent creates a vehicle record. When they book an oil change, a milestone and schedule entry appear. No schema design, no migrations, no manual writes needed.

Each end user gets their own scoped data: vehicles, documents (insurance, registration, receipts), milestones (service events, maintenance), and schedules (upcoming appointments). All of this is queryable through the REST API and manageable through the manage-data tool.

The manage-data layer works across six table families: end_users, vehicles, vehicle_groups, documents, milestones, and schedules. The sections below describe what each table is for so your own code and agents do not write the wrong kind of record.

Every conversation automatically loads the user's current vehicles, upcoming schedules, recent milestones, and documents into the agent's context. The agent just knows what the user has. This is not a retrieval step you configure. It happens automatically.

Real-time data events

When the agent creates, updates, or deletes data, your client receives a data_saved event via the SSE stream. This lets your UI update in real time without polling.

The event contains a mutations array. Each mutation includes the action that was performed, the table that was affected, and the full record after the change.

data_saved event shape
{
  "type": "data_saved",
  "mutations": [
    {
      "action": "create",
      "table": "vehicles",
      "record": {
        "id": "a1b2c3d4-...",
        "year": 2022,
        "make": "Honda",
        "model": "Civic",
        "trim": "EX",
        "odometer_km": 34500,
        "created_at": "2026-03-31T14:22:00Z"
      }
    }
  ]
}

Handle this event on the client to keep your UI in sync as the agent works:

Handling data_saved in your SSE client
for (const line of lines) {
  if (!line.startsWith("data: ")) continue;
  const payload = line.slice(6);
  if (payload === "[DONE]") break;

  const event = JSON.parse(payload);

  if (event.type === "data_saved") {
    for (const mutation of event.mutations) {
      switch (mutation.table) {
        case "vehicles":
          refreshVehicleList(mutation.record);
          break;
        case "schedules":
          addToCalendar(mutation.record);
          break;
        case "milestones":
          updateServiceHistory(mutation.record);
          break;
      }
    }
  }
}

REST API and managed data are the same records

All data is also accessible via the REST API. The managed data layer and the REST API operate on the same records. Use the REST API for bulk operations, dashboards, and integrations outside of chat.

REST API Reference

Galadri stores structured data on behalf of your application's users. All data is scoped to your organization and a specific end user. Data can be created and managed in two ways:

  • Via the AI agent — The manage-data tool lets agents create, read, update, list, and delete records through natural conversation.
  • Via REST endpoints — Use the Users, Vehicles, Vehicle Groups, Documents, Milestones, Schedules, Sessions, and Session Messages endpoints for direct programmatic access.

REST endpoint matrix

FieldTypeDescription
/v1/usersGET, POSTList end users or upsert an end-user profile by external_id.
/v1/users/:idGET, PATCH, DELETERead, update, or soft-delete one end user by Galadri UUID.
/v1/vehiclesGET, POSTList or create vehicles for an end user.
/v1/vehicles/:idGET, PATCH, DELETERead, update, or soft-delete one vehicle by UUID.
/v1/vehicle-groupsGET, POSTList or create named vehicle collections for an end user.
/v1/vehicle-groups/:idGET, PATCH, DELETERead, update, or soft-delete one vehicle group by UUID.
/v1/documentsGET, POSTList or create document metadata records for an end user.
/v1/documents/:idGET, PATCH, DELETERead, update, or soft-delete one document by UUID.
/v1/milestonesGET, POSTList or create service history and due-item records for an end user.
/v1/milestones/:idGET, PATCH, DELETERead, update, or soft-delete one milestone by UUID.
/v1/schedulesGET, POSTList or create calendar entries, reminders, and automation schedules for an end user.
/v1/schedules/:idGET, PATCH, DELETERead, update, or soft-delete one schedule by UUID.
/v1/sessionsGETList chat sessions, optionally filtered by end user.
/v1/sessions/:idGET, PATCH, DELETERead a session, update hidden/unread metadata, or soft-delete it.
/v1/sessions/:id/messagesGETList messages for one chat session.

Metric units

All measurements are stored in metric units (kilometers, liters, Celsius). The AI agent converts to the user's preferred units in conversation based on their profile settings.

Manage-data Table Guide

FieldTypeDescription
end_usersprofileThe end user's profile, preferences, contact info, and communication opt-ins. This is the root record for everything else. In manage-data, it is auto-scoped to the current user.
vehiclesassetPer-vehicle facts such as make/model/year, odometer, registration details, and lightweight estimate fields used for UI shorthand.
vehicle_groupsorganizationNamed collections of vehicles like fleets, family cars, or work trucks. Use these when schedules or reminders should target multiple vehicles together.
documentsfile metadataMetadata and extracted analysis for uploaded or linked files such as receipts, inspections, insurance cards, registrations, titles, and condition photos.
milestoneshistory / due itemService history and ownership lifecycle events. Use milestones for completed work and upcoming due items, not for generic reminders.
schedulescalendar / automationAppointments, reminders, recurring events, and AI-triggered outbound communications. Use schedules for things that happen at a date/time or should fire automatically.

Communication continuity records

Galadri also maintains communication-specific records outside the manage-data tables above. These are runtime continuity tables for email, SMS, and phone workflows, not user-authored business objects.

FieldTypeDescription
chat_sessions / messagesconversation memoryEnd-user chat history. Messages store organization_id, chat_session_id, and denormalized end_user_id so conversation-history retrieval, retention, and user-scoped governance can filter directly before returning bounded snippets.
email_threads / email_messagescommunicationOutbound and inbound email history, reply tracking, and the sender identity used for each thread.
sms_threads / sms_messagescommunicationOutbound and inbound SMS or MMS history for a specific user and phone-number pair.
call_sessions / voice_threadscommunicationInbound and outbound voice-call history plus durable voice continuity threads. Callback voice threads can link to either phone-call callback objectives or SMS callback objectives when an outside business switches channels.
voice_callback_objectivescallback continuityDestination-scoped callback objectives for outbound phone calls placed to external contacts on behalf of a user. They track the owning Twilio credential, active-query state, status, and follow-up channel.
comm_callback_objectivescallback continuityDestination-scoped email and SMS callback cases for external contacts. These stay durable and auditable, track whether Galadri is actively waiting on a response, store the user follow-up channel to use when the callback resolves, and remain available in a short recent-resolved window for continuity.
external_contact_conversationscallback continuityRestricted external email or phone/text conversations keyed by the destination credential plus the outside sender identity. They are created only when a scoped callback candidate exists or is bound, then track the active case, recent candidates, rolling summary, and continuity state while the AI clarifies or continues work on behalf of a user.
external_contact_conversation_eventscallback auditAppend-only audit events for external-contact callback state transitions, including clarification requests and when a conversation binds to, switches between, reopens, or resolves an email, SMS, or voice callback case.

End Users

End users are your application's users. Each end user gets their own scoped data. Users are identified by the end_user_id you provide in chat requests. If the user does not exist, one is created automatically.

POST/v1/users

Create or update an end user. If a user with the same external_id already exists in your organization, it is updated (upsert).

FieldTypeDescription
external_idstringYour application's user ID. Used as the upsert key.
first_namestringUser's first name.
last_namestringUser's last name.
emailstringEmail address.
phonestringPhone number in E.164 format.
user_type"individual" | "business"Type of user account.
street_addressstringStreet address.
citystringCity name.
statestringState or province.
zipstringZIP or postal code.
countrystringCountry name or ISO code.
timezonestringIANA timezone (e.g., "America/New_York").
distance_unit"km" | "mi"Preferred distance unit.
volume_unit"liters" | "gallons"Preferred volume unit.
currencystringCurrency code (e.g., "USD").
temperature_unit"celsius" | "fahrenheit"Preferred temperature unit.
pressure_unit"kpa" | "psi" | "bar"Preferred pressure unit.
speed_unit"kph" | "mph"Preferred speed unit.
fuel_economy_unit"l_per_100km" | "mpg" | "km_per_l"Preferred fuel economy unit.
marketing_opt_inbooleanMarketing email consent.
sms_opt_inbooleanSMS consent.
call_opt_inbooleanPhone call consent.
email_opt_inbooleanEmail communication consent.
ready_to_buy_sell_scorenumberAI-authored 0.0-1.0 score estimating buying or selling readiness. Use fractions, not percentages.
other_archival_memorystringLong-term profile memory that does not fit another structured field.
GET/v1/users

List end users with filtering, sorting, and pagination.

Query parameters

ParameterTypeDescription
limitintegerResults per page (1-100, default 20).
offsetintegerNumber of results to skip (0-based).
sortstringSort field: created_at, updated_at, first_name, last_name, email, external_id.
orderstring"asc" or "desc".
filter.*stringFilter by column. E.g., filter.city=Phoenix&filter.user_type=individual. Filterable: external_id, email, phone, first_name, last_name, city, state, country, user_type.
Response
{
  "data": [
    {
      "id": "a1b2c3d4-...",
      "external_id": "user-123",
      "first_name": "Jane",
      "last_name": "Smith",
      "email": "jane@example.com",
      "city": "Phoenix",
      "state": "AZ",
      "timezone": "America/Phoenix",
      "distance_unit": "mi",
      "created_at": "2026-03-20T10:30:00Z",
      "updated_at": "2026-03-20T10:30:00Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 20,
    "offset": 0,
    "has_more": false
  }
}

Vehicles

POST/v1/vehicles

Create a vehicle for an end user. The user_id field is your application's external ID for the end user (the same value you pass as end_user_id in chat requests).

FieldTypeDescription
user_idstring (required)Your application's external ID for the end user.
vinstringVehicle Identification Number (17 characters).
yearintegerModel year.
makestringManufacturer (e.g., "Toyota").
modelstringModel name (e.g., "Camry").
trimstringTrim level (e.g., "SE").
colorstringExterior color.
nicknamestringDisplay name (e.g., "Mom's car").
license_platestringLicense plate number.
powertrain"bev" | "hybrid" | "ice"Battery electric, hybrid, or internal combustion.
ownership_status"owned" | "leased" | "financed" | "other"How the vehicle is owned.
service_bias"cheap" | "moderate" | "premium"How price-sensitive the owner tends to be for service decisions.
odometer_kmnumberCurrent odometer reading in kilometers.
tank_capacity_litersnumberFuel tank capacity in liters.
ev_battery_capacity_kwhnumberEV battery capacity in kilowatt-hours.
est_kwh_per_100kmnumberEstimated energy consumption for BEVs. This is a ballpark AI-authored shorthand field, not a verified telemetry reading.
est_liters_per_100kmnumberEstimated fuel consumption for ICE or hybrid vehicles. This is a ballpark AI-authored shorthand field, not a verified measurement.
est_maintenance_per_100km_centsintegerEstimated maintenance cost per 100 km in USD cents. This is an AI-authored ballpark for the typical make/model/year and broad condition, used as UI shorthand rather than exact accounting.
image_urlsstring[]URLs to vehicle photos.

Estimated vehicle fields

Fields prefixed with est_ are intentionally approximate. They are usually written by the agent as ballpark values from the make, model, year, powertrain, and any major condition clues. They are meant for quick UI reference, not as authoritative vendor lookups, telemetry, or personalized total cost accounting.

GET/v1/vehicles

List vehicles for an end user. The user_id query parameter is required.

Query parameters

ParameterTypeDescription
user_idrequiredstringExternal ID of the end user.
limitintegerResults per page (1-100, default 20).
offsetintegerNumber of results to skip.
sortstringSort field: created_at, updated_at, year, make, model, nickname.
orderstring"asc" or "desc".
filter.*stringFilter by column: vin, year, make, model, trim, color, powertrain, ownership_status, nickname.

Sessions

GET/v1/sessions

List chat sessions. Sessions are created automatically by the chat endpoint. Use PATCH /v1/sessions/:id to update hidden or unread, DELETE /v1/sessions/:id to soft-delete, and GET /v1/sessions/:id/messages to read messages.

Query parameters

ParameterTypeDescription
user_idstringFilter to a specific end user (external ID).
limitintegerResults per page (1-100, default 20).
offsetintegerNumber of results to skip.
sortstringSort field: created_at, updated_at, title.
filter.hiddenbooleanFilter by hidden status.
filter.agent_idstringFilter by agent UUID.

Vehicle Groups

Vehicle groups let users organize vehicles into named collections (e.g., "Fleet A", "Family cars"). Groups are managed through the manage-data tool in chat or through the Vehicle Groups REST endpoints.

FieldTypeDescription
idUUIDAuto-generated record ID.
namestringGroup name.
descriptionstringOptional description.
vehicle_idsUUID[]Array of vehicle IDs in the group.

Documents

Documents store metadata about files associated with vehicles or end users (receipts, insurance cards, registration papers, etc.).

FieldTypeDescription
idUUIDAuto-generated record ID.
vehicle_idsUUID[]Associated vehicles. Documents can link to more than one vehicle.
titlestringDocument title.
typestringDocument type: "receipt", "insurance", "registration", "title", "inspection", "warranty", "loan", "lease", or "other".
file_urlstringURL to the stored file.
key_infostringSummary of key information extracted from the document.
full_analysisstringDetailed analysis of the document content.
notesstringUser or agent notes.

Milestones

Milestones track service events, maintenance, and other vehicle lifecycle events.

A milestone can begin as an upcoming due item and later be updated with completion details when the work actually happens. Agents should prefer updating a matching open milestone before creating a duplicate completion record.

FieldTypeDescription
idUUIDAuto-generated record ID.
vehicle_idsUUID[]Associated vehicles. Milestones can link to more than one vehicle.
document_idsUUID[]Related documents such as receipts, reports, photos, or inspection forms.
typestringEvent type: "oil", "12v_battery", "tire_rotation", "tire_replacement", "registration", "inspection", "sale", "insurance_renewal", "drivers_license_renewal", or "other".
titlestringDisplay title.
completion_utcISO 8601When the milestone was completed.
completion_kmnumberOdometer at completion (km).
completion_price_centsintegerCost in cents.
next_due_utcISO 8601When the next occurrence is due.
next_due_kmnumberOdometer reading when next due.
performed_by_namestringName of the service provider.

Schedules

Schedules represent upcoming appointments, events, and automated communications. A schedule can be a simple calendar entry or an AI-triggered action that sends reminders via email, SMS, voice call, or webhook.

FieldTypeDescription
idUUIDAuto-generated record ID.
vehicle_idsUUID[]Associated vehicles.
vehicle_group_idsUUID[]Associated vehicle groups.
titlestringEvent title (required).
descriptionstringEvent description.
attendeesstring[]Attendee email addresses.
special_instructionsstringSpecial instructions for the appointment.
start_time_utcISO 8601 with timezone offsetWhen the actual event occurs. Required for calendar events, optional for pure automations.
end_time_utcISO 8601 with timezone offsetEvent end time.
location_namestringVenue or business name.
location_addressstringFull address.
location_phonestringLocation phone number.
location_emailstringLocation email.
recurringbooleanWhether this event repeats.
frequency_typestringRecurrence frequency: "hourly", "daily", "weekly", "monthly", or "yearly".
frequency_intervalintegerRecurrence interval (e.g., 2 for every 2 weeks).
odometer_kmintegerOdometer threshold for an odometer-triggered schedule.
odometer_triggerbooleanWhether this schedule fires when a vehicle crosses odometer_km instead of at a fixed calendar time.
agent_runtime_utcISO 8601 with timezone offsetWhen the AI agent should fire. Separate from start_time_utc. See Scheduled Communications.
agent_runtime_promptstringThe prompt the agent executes at agent_runtime_utc.
agent_comm_channelstringDelivery channel: "email", "sms", "call", or "webhook". Null means no AI delivery.
agent_idUUIDWhich agent handles this schedule. Falls back to the org's first active agent if null.
is_completedbooleanWhether the event is completed.

agent_runtime_utc vs start_time_utc

These are intentionally separate. An oil change scheduled for September 8 at 8am (start_time_utc) might trigger the AI three days earlier (agent_runtime_utc = September 5) to send a reminder. See the Scheduled Communications section for the full execution flow.

Automation-only schedules

Creating a schedule without start_time_utc is valid for pure automations. Provide agent_runtime_prompt, agent_comm_channel, and either agent_runtime_utc or an odometer trigger with odometer_km.

CRUD via Chat

Agents with the manage-data tool can create, read, update, list, and delete records in six table families. end_users is profile-scoped to the current user, while the other tables create separate records. The agent issues a tool call with the manage-data slug inside the batched actions:

Example: Agent creates a vehicle
{
  "actions": [
    {
      "tool": "manage-data",
      "args": {
        "action": "create",
        "table": "vehicles",
        "data": {
          "year": 2020,
          "make": "Toyota",
          "model": "Camry",
          "trim": "SE",
          "vin": "4T1BF1FK5LU123456",
          "odometer_km": 72420
        }
      }
    }
  ]
}

Available actions:

FieldTypeDescription
describeactionReturn the writable schema for a table before creating or updating. Use this when you need an authoritative field list.
createactionCreate a new record. Requires table and data.
readactionRead a single record by ID. Requires table and id.
listactionList records with optional filters. Requires table. Optional: filters, limit (max 50).
updateactionUpdate fields on an existing record. Requires table, id, and data.
deleteactionSoft-delete a record. Requires table and id.

Estimate fields are the exception

Most fields in these tables should come from the user, uploaded files, API lookups, or concrete workflow results. The main exception is the small set of vehicle fields prefixed with est_, which are intentionally allowed to be agent-authored ballpark values for UI shorthand.

The data_saved Event

When the agent creates or updates data, the stream emits a data_saved event with the full record plus mutation metadata. Use this to update your UI in real-time without polling.

Handling data_saved events
// In your SSE handler
if (event.type === "data_saved") {
  for (const mutation of event.mutations) {
    const changedFields = mutation.changedFields || [];

    if (mutation.table === "vehicles" && mutation.action === "create") {
      // Add the new vehicle to your UI
      addVehicleToList(mutation.record);
    }
    if (mutation.table === "schedules" && mutation.action === "create") {
      // Show the new appointment in the calendar
      addToCalendar(mutation.record);
    }
    if (mutation.action === "update") {
      // Optionally show a compact diff card using changedFields
      showDataMutationCard(mutation.record, changedFields);
    }
  }
}

Building a data-driven UI

Combine the REST endpoints (for initial data loading and direct CRUD) with data_saved events (for real-time updates during AI conversations) to build a responsive data layer in your application.

Data Access Permissions

Each agent can be configured with a data access level that controls what the manage-data tool can do. See Data Access for details.

FieldTypeDescription
read_writedefaultFull CRUD access. The agent can create, read, update, and delete records.
readrestrictedRead and list only. Create, update, and delete actions are blocked.
nonerestrictedNo data access. The manage-data tool is not available.

System Fields

Every record includes these auto-managed fields. You do not need to set them when creating records.

FieldTypeDescription
idUUIDUnique identifier. Auto-generated on create.
organization_idUUIDYour organization ID. Set automatically from the API key.
end_user_idUUIDInternal ID of the end user. Set automatically from the session context.
created_atISO 8601Timestamp when the record was created.
updated_atISO 8601Timestamp when the record was last updated.
deleted_atISO 8601 | nullSoft delete timestamp. Null if the record is active.