Service Call Model
How @downcity/city unifies AI service, custom service, and service calls.
The easiest mistake with City is to notice only client.ai.* and assume the access layer is just "an AI SDK."
The more accurate model is:
client.ai.*: AI service callsclient.service(...).action(...).invoke(): generic Action callsclient.service(...).get(...): read-oriented GET Action calls
AI service
Use this for:
text()stream()image()video()
These should usually go through AIService first because AIService already unifies the model catalog, default model selection, and AI SDK UIMessage contracts.
Custom service
This is a business service you registered into City yourself.
For example:
const result = await client.service("rewrite").action("formal").invoke({
prompt: "Rewrite this in a more professional tone",
});This is a better fit for:
- workflow actions
- city business actions
- capabilities where you do not want to expose raw model details directly
Official service
A service is not a separate API family. It is still just a service mounted inside City.
For example:
accountsusagepaymentpayment
So the calling pattern stays the same:
await client.service("accounts").action("login").invoke(...)
await client.service("usage").get("me")
await client.service("payment").get("methods")Why this unified model matters
Because a city frontend should not need to learn three separate systems:
- one for AI
- one for business services
- one for services
Downcity is designed to compress all of them back into one call mental model.