Define an API product
Define an API product from the records of a source or from a schema of Nouns, check it, and see every Interface the platform derives from it.
To define an API Product, write its Definition as data, from the records of a source or from a schema of Nouns. The platform checks it and derives every Interface from it.
Prerequisites
None: the steps need no account and no install.
Steps
A lookup over the records of a source
To start, describe where the records come from as one Wrapped Source descriptor. Countrylookup reads one dataset file, and its transform shapes each record as data:
selectpicks and renames a field, andcoercesets its type.countrylookup.example: the descriptor slug: countries title: Countrylookup description: Countries by ISO code,read from one public dataset file. origin: https: //atlas.example resources: countries: list: path: /countries.json method: GET extract: itemId: cca2 transform: - { op: select, path: cca2, as: code } - { op: select, path: name.common, as: name } - { op: select, path: capital.0, as: capital } - { op: select, path: subregion } - { op: select, path: area } - { op: coerce, field: area, to: number } schedule: cron: 0 6 * * 1 tenancy: owner: countrylookup.example visibility: third-party discovery: home: https: //countrylookup.example To serve lookups over the records, write the Definition. Each Operation reads the
countriescollection and posts its rate:getCountryby code,listCountriesby subregion.countrylookup.example: the Definition { "startup": "countrylookup.example", "product": "countrylookup", "name": "Countrylookup", "description": "Countries by ISO code or subregion,with name, capital and area.", "version": "1.0.0", "currency": "USD", "free": 1000, "operations": [ { "verb": "getCountry", "method": "GET", "path": "/countries/ {code}", "summary": "One country", "description": "Answers one country by its ISO 3166-1 alpha-2 code.", "params": [ { "name": "code", "in": "path", "type": "string", "required": true, "description": "The upper-case alpha-2 code", "example": "FR" } ], "returns": { "type": "object", "properties": { "code": { "type": "string" }, "name": { "type": "string" }, "capital": { "type": "string" }, "subregion": { "type": "string" }, "area": { "type": "number" } } }, "example": { "input": { "code": "FR" }, "output": { "code": "FR", "name": "France", "capital": "Paris", "subregion": "Western Europe", "area": 551695 } }, "price": 0.001, "run": { "kind": "get", "collection": "countries", "key": "code" } }, { "verb": "listCountries", "method": "GET", "path": "/countries", "summary": "Countries in one subregion", "description": "Lists every country in one subregion, each with its code.", "params": [ { "name": "subregion", "in": "query", "type": "string", "required": true, "description": "A subregion as the records name it", "example": "Western Europe" } ], "returns": { "type": "array", "items": { "type": "object", "properties": { "code": { "type": "string" }, "name": { "type": "string" } } } }, "example": { "input": { "subregion": "Western Europe" }, "output": [{ "code": "FR", "name": "France" }, { "code": "NL", "name": "Netherlands" }] }, "price": 0.005, "run": { "kind": "list", "collection": "countries", "filters": ["subregion"] } } ] } To see what the platform derives, read where the Definition answers on its own host.
Derived At API: getCountryhttps://countrylookup.example/ countries/ {code} API: listCountrieshttps://countrylookup.example/ countries MCP https://countrylookup.example/ mcp Docs https://countrylookup.example/ docs SDK https://countrylookup.example/ sdk App https://countrylookup.example/ dashboard OpenAPI 3.1 https://countrylookup.example/ openapi.json llms.txt https://countrylookup.example/ llms.txt Markdown twin https://countrylookup.example/ docs/ {verb}.md agents.json https://countrylookup.example/ .well-known/ agents.json Pricing document https://countrylookup.example/ pricing
An API from a schema of Nouns
To start from a schema, write the Nouns, their fields and the Verbs on them. A field is a type,
!marks a required field, and->links another Noun.openhours.example: the Nouns and Verbs nouns: Place: placeId: string! name: string city: string Hours: placeId: ->Place timezone: string open: "[Span]" verbs: Place.find: answers: list by: city path: /places Hours.get: answers: one by: placeId path: /places/{placeId}/ hours To derive the Definition, give each Verb its rate in the Offer. Each Verb derives one operation, named by its Noun and Verb:
Place.findanswers a list, so it isfindPlaces.openhours.example: the Definition { "startup": "openhours.example", "product": "openhours", "name": "Openhours", "description": "Opening hours for places,by place id.", "version": "1.0.0", "currency": "USD", "free": 1000, "requiresKey": true, "operations": [ { "verb": "findPlaces", "method": "GET", "path": "/places", "summary": "Places in one city", "description": "Lists the places Openhours holds in one city, by the city name, each with its place id.", "params": [ { "name": "city", "in": "query", "type": "string", "required": true, "description": "The city name", "example": "Lisbon" } ], "returns": { "type": "array", "items": { "type": "object", "properties": { "placeId": { "type": "string" }, "name": { "type": "string" } } } }, "example": { "input": { "city": "Lisbon" }, "output": [{ "placeId": "pl_4821", "name": "Cafe Alfama" }] }, "price": 0.002, "run": { "kind": "list", "collection": "places", "filters": ["city"] } }, { "verb": "getHours", "method": "GET", "path": "/places/ {placeId}/ hours", "summary": "The opening hours of one place", "description": "Answers the weekly opening hours of one place, in the place's own timezone.", "params": [ { "name": "placeId", "in": "path", "type": "string", "required": true, "description": "The place id", "example": "pl_4821" } ], "returns": { "type": "object", "properties": { "placeId": { "type": "string" }, "timezone": { "type": "string" }, "open": { "type": "array" } } }, "example": { "input": { "placeId": "pl_4821" }, "output": { "placeId": "pl_4821", "timezone": "Europe/ Lisbon", "open": [{ "day": "mon", "from": "09: 00", "to": "18: 00" }] } }, "price": 0.002, "run": { "kind": "get", "collection": "hours", "key": "placeId" } } ] } To check it, hold it to the rules every Definition passes. The platform runs the same check when a Definition is written and again when it is read, so a Definition with a problem never serves.
To see what the check answers, break two rules: capitalize the first verb and move its path under
/docs. The check names each mistake, one line per problem.The problems, one per mistake [ "FindPlaces:a verb is lowerCamelCase", "FindPlaces: /docs/ places is one of the generated doors" ] To see what the platform derives, read where Openhours answers on its own host.
Derived At API: findPlaceshttps://openhours.example/ places API: getHourshttps://openhours.example/ places/ {placeId}/ hours MCP https://openhours.example/ mcp Docs https://openhours.example/ docs SDK https://openhours.example/ sdk App https://openhours.example/ dashboard OpenAPI 3.1 https://openhours.example/ openapi.json llms.txt https://openhours.example/ llms.txt Markdown twin https://openhours.example/ docs/ {verb}.md agents.json https://openhours.example/ .well-known/ agents.json Pricing document https://openhours.example/ pricing
Result
Countrylookup and Openhours are each one Definition that passes every check. Every Interface reads that one Definition, so no two of them can disagree, and a call that misses or fails costs nothing.
Next steps
- Wrapped Source: every member of a descriptor.
- Product: every member of a Definition.
- Offers: the posted rates and the free calls.
- Your API on your own domain: where every Interface answers.