OpenTrip Docs
BackendHTTP API

DTO catalog

DTO catalog

StopCategory

"Sight" | "Food" | "Stay" | "Shopping" | "Activity" |
"Walk" | "Park" | "Transit" | "Plan"

TripStatus

"active" | "planning" | "settled"

MemberRole

"owner" | "editor" | "viewer"

TripSummary

Returned by GET /api/trips.

FieldTypeMeaning
idstringTrip id
titlestringTitle
startLabelstringDisplay start label
endLabelstringDisplay end label
statusTripStatusStatus
currencystringTrip currency code
coverColorstringCard color
coverUrlstring | nullOptional Unsplash (or other) cover image URL
memberCountnumberMembers
stopCountnumberStops
createdAtstringISO 8601 creation time
creatorNamestringOwner / first member display name
membersTripSummaryMember[]Creator-first; for avatar stack
location{ lat, lng } | nullFirst located stop, or null

TripSummaryMember: id, name, initials, avatarBg, avatarFg, optional image, isCurrentUser.

TripDto (full trip)

Returned by most trip mutations and GET /api/trips/:id.

FieldTypeMeaning
idstringTrip id
titlestringTitle
statusstring (TripStatus)Status
currencystringDefault trip currency
startDatestringISO YYYY-MM-DD or ""
coverUrlstring | nullOptional cover image URL
intakeTripIntake | nullCreate-wizard answers (TBD fields omitted)
agentSeedPendingbooleanPlanner should offer a one-shot suggested @agent draft
membersMemberDto[]Membership
permissionsTripPermissionsCaller’s effective rights
daysDayDto[]Itinerary days
stopsStopDto[]Flat list (use day + order)
expensesExpenseDto[]Expenses
budgetBudgetServer-computed settlement

TripIntake (create wizard; omitted keys mean TBD):

FieldTypeMeaning
destinationstring?City / region label
destinationLatnumber?Geocoded destination latitude (create / backfill)
destinationLngnumber?Geocoded destination longitude
dayCountnumber?Planned day count
startDatestring?ISO start
endDatestring?ISO inclusive end
budgetAmountnumber?Planned budget amount
budgetCurrencystring?Currency for budgetAmount
partySizenumber?Planned party size (does not create members)

TripPermissions:

FieldTypeMeaning
isMemberbooleanMay read
canEditbooleanOwner/editor (not viewer)
canInvitebooleanMay create invites

MemberDto:

FieldTypeMeaning
idstringTrip-local member id
namestringDisplay name
shortNamestringShort label
initialsstringAvatar initials
avatarBg / avatarFgstringFallback colors
imagestring | null?Avatar URL
userIdstring | null?Better Auth user id; null for demo
roleMemberRoleCollaboration role
canInvitebooleanInvite permission
isCurrentUserbooleanTrue for the requesting user

DayDto:

FieldTypeMeaning
numbernumber1-based day number
datestringISO YYYY-MM-DD or ""
dateLabelstringLegacy display label
citystringCity / region label
colorstringHex color

StopDto (persistence-only order is not exposed):

FieldTypeMeaning
idstringStop id
daynumberDay number
timestringTime label
durationstringe.g. 1h
namestringTitle
areastringArea / neighborhood
categorystring (StopCategory)Category
lat / lngnumberCoordinates
costnumberPer-person estimate (minor units)
costCurrencystringISO code; empty → trip currency
createdBystringTrip-local member id
transitbooleanTransit segment flag
notestringMarkdown note
votesstring[]Member ids who voted
comments{ author, timeLabel, text }[]Comments

ExpenseDto (createdOrder not exposed):

FieldTypeMeaning
idstringExpense id
descriptionstringLabel
payerstringTrip-local member id
amountnumberMinor units
currencystringISO code for amount
categorystring (StopCategory)Type
participantsstring[]Split among these member ids
whenLabelstringDisplay time label

Budget

FieldTypeMeaning
totalnumberSum of expense amounts
perPersonnumbertotal / memberCount (rounded)
balancesBalance[]Per-member paid / share / net
settlementsSettlement[]Minimal transfer plan

Balance: { memberId, paid, share, net }net = paid − share (positive ⇒ others owe them).
Settlement: { from, to, amount } — transfer from debtor member id to creditor.
Note: Budget math sums numeric amounts without FX conversion; mixed currencies are stored for display / future FX. Use GET /api/fx/rates only for display conversion of settlements.

Example:

{
  "total": 470200,
  "perPerson": 117550,
  "balances": [
    { "memberId": "lynn", "paid": 91200, "share": 117550, "net": -26350 }
  ],
  "settlements": [
    { "from": "lynn", "to": "sam", "amount": 26350 }
  ]
}

InvitePreview

FieldTypeMeaning
tripIdstringTrip to join
tripTitlestringTitle
inviterNamestringWho invited
memberCountnumberCurrent members
role"editor" | "viewer"Role on accept
accessScope"anyone" | "restricted_emails"Link policy
status"usable" | "expired" | "revoked" | "email_restricted"Usability
alreadyMemberbooleanViewer already on trip
expiresAtstring | nullExpiry

UserPreferenceDto

FieldTypeMeaning
userIdstringUser id
plannerSidebar{ width: number; collapsed: boolean }Planner chrome
agentPanelCollapsedbooleanAgent panel UI
updatedAtstringISO timestamp

WeatherData

FieldTypeMeaning
iconstringIcon code
mainstringShort condition
descriptionstringLonger description
tempnumberTemperature
feelsLikenumberFeels-like
humiditynumberHumidity
pressurenumberPressure
visibilitynumberVisibility
windSpeednumberWind speed
windDegnumberWind direction deg
cloudsnumberCloud cover

FxRatesData

FieldTypeMeaning
datestringRate table date
basestringBase currency
providerstringProvider id
ratesRecord<string, number>Quote → units of quote per 1 base (includes base: 1)
fetchedAtstringFetch timestamp

Agent DTOs

AgentHistoryDto

{ messages: AgentMessageDto[]; suggestions: AgentSuggestionDto[] }

AgentPostMessageResultDto

Returned by POST …/agent/messages (inside { data }):

{ addressed: boolean; message: AgentMessageDto }

message is the inserted row so clients can update the history cache without an immediate list GET (Hyperdrive may serve a stale cached SELECT).

AgentEventsDto

{
  latestSeq: number;
  messages: AgentMessageDto[];
  suggestions: AgentSuggestionDto[];
}

AgentMessageDto

FieldTypeMeaning
idstringMessage id (may match client UIMessage id)
seqnumberMonotonic poll cursor
role"user" | "assistant" | "system"Role
partsAgentMessagePart[]Text and/or richer AI SDK parts
actorUserIdstring | nullHuman author; null for agent
actorNamestring | nullResolved from trip membership
source"chat" | "mention" | "operation" | "threshold" | "stop_comment"Origin
mentionedUserIdsstring[]@mentioned Better Auth user ids
createdAtstringISO time

Text part: { type: "text"; text: string }. Assistant messages may include tool / approval parts with additional fields. Agent UI uses these typed, persistent data parts (neither is transient):

type AgentGroundingPart = {
  type: "data-agent-grounding";
  id: string;
  data: {
    kind: "street-view";
    outcome:
      | "found"
      | "empty"
      | "place_not_found"
      | "invalid_request"
      | "service_unavailable";
    request?:
      | { kind: "place"; query: string; language: "en" | "zh"; selectionIndex: number }
      | { kind: "coordinate"; lat: number; lng: number; language: "en" | "zh"; selectionIndex: number };
    placeLabel?: string;
    imageIds: string[];
    selectedImageId?: string;
  };
};

type AgentStatusPart = {
  type: "data-agent-status";
  id: string;
  data: {
    kind: "generated-ui-fallback";
    reason: "place_not_found" | "invalid_request" | "service_unavailable";
    retryable: boolean;
    retryRequest?: { request: AgentGroundingPart["data"]["request"] };
  };
};

Only outcome="found" grounding image ids authorize a street-view card.

AgentSuggestionDto

FieldTypeMeaning
idstringSuggestion id
messageIdstring | nullRelated session message
status"pending" | "applied" | "stale" | "expired"Lifecycle
severity"info" | "warning" | "critical"Urgency
reasonstringModel reason
suggestionTextstringHuman-readable suggestion
patchPendingPatchDiscriminated patch (see below)
expiresAtstring | nullExpiry
appliedBystring | nullWho applied
createdAt / updatedAtstringISO times

PendingPatch

Discriminated by kind (must match trip ops the aggregate supports):

kindPayload
rename_triptitle
add_day(none)
delete_daydayNumber
update_daydayNumber, changes (same as day PATCH body)
reorder_daysorder: number[]
insert_stopdraft (same as insert stop body)
update_stopstopId, changes
append_stop_notestopId, markdown
move_stopmove: { stopId, day, index }
add_expensedraft (expense body)
update_expenseexpenseId, changes

Applying a patch always goes through domain trip methods after human approval.

StreetViewImageDto

FieldTypeMeaning
idstringOpaque image id
coordinate{ lat, lng }Capture coordinate
distanceMetersnumber, optionalRounded distance from search origin
capturedAtISO string, optionalCapture time
headingDegreesnumber, optionalCamera heading
supports360booleanInteractive panorama availability
previewUrlstringSame-origin, trip-scoped preview route
attribution{ label, url? }Trusted provider attribution

Provider thumbnail URLs and access tokens are never part of this DTO.

ReservationDto

FieldTypeMeaning
id, tripIdstringReservation and owning trip ids
typeflight | accommodation | restaurant | rail | ground_transport | activity | otherBooking kind
statustentative | confirmed | cancelled | completedLifecycle
title, provider, confirmationNumberstringBooking identity
startAt, endAtISO string / nullUTC instants
timezonestringDisplay IANA timezone
locationName, addressstringDisplay location
latitude, longitudenumber / nullOptional coordinate pair
dayNumber, stopId, expenseIdnumber/string / nullOptional trip links
amountMinor, currencynumber/string / nullOptional booking amount
notesstringMember notes
createdBy, createdAt, updatedAtstringAudit fields
revisionnumberIf-Match concurrency token

← API index

On this page