openvibers/openvibe.contracts Commits
Contracts 0.147.0: work.application.manage and food.recipes.suggest (#103)
Changes
@@@ -4,6 +4,15 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.147.0 — 2026-10-12++++- `work.application.manage` (owner work): a person's tracked job applications on OpenVibe.Work, for apps and agents++ (`work.application@1`, `work.application-write-request@1`, `work.application-result@1`). Asking the person's++ OpenVibe.Actor agent about one is the person's own act and takes no capability. The work manifest lists it.++- `food.recipes.suggest` (owner food): recipe ideas from the caller's pantry through OpenVibe.AI's++ `food.suggest_recipes`, priced from Food's own food list (`food.recipe-suggest-request@1`,++ `food.recipe-suggestion@1`, `food.recipe-result@1`). The food manifest lists it.++## 0.146.0 — 2026-10-12- `sources.source@1` and `sources.source-write-request@1`: an `api` endpoint may declare `where` (up to 10 path →@@@ -6802,6 +6802,60 @@"visibility": "first-party","compatibility": "backward","status": "active"++ },++ {++ "id": "work.application",++ "version": "1.0.0",++ "owner": "work",++ "schema": "work/application.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "work.application-write-request",++ "version": "1.0.0",++ "owner": "work",++ "schema": "work/application-write-request.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "work.application-result",++ "version": "1.0.0",++ "owner": "work",++ "schema": "work/application-result.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.recipe-suggest-request",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/recipe-suggest-request.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.recipe-suggestion",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/recipe-suggestion.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"++ },++ {++ "id": "food.recipe-result",++ "version": "1.0.0",++ "owner": "food",++ "schema": "food/recipe-result.v1.json",++ "visibility": "public",++ "compatibility": "backward",++ "status": "active"}]}nnew file mode 100644@@@ -0,0 +1,27 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/recipe-result.v1.json",++ "title": "FoodRecipeResult",++ "description": "food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).",++ "anyOf": [++ {++ "$ref": "recipe-suggestion.v1.json"++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "recipes"++ ],++ "properties": {++ "recipes": {++ "type": "array",++ "maxItems": 20,++ "items": {++ "$ref": "recipe-suggestion.v1.json"++ }++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,19 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/recipe-suggest-request.v1.json",++ "title": "FoodRecipeSuggestRequest",++ "description": "food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "ask": {++ "type": "string",++ "maxLength": 500++ },++ "people": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ }++ }++}nnew file mode 100644@@@ -0,0 +1,176 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/recipe-suggestion.v1.json",++ "title": "FoodRecipeSuggestion",++ "description": "food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "created_at",++ "ask",++ "people",++ "ai_run_id",++ "recipes"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^rcp_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "ask": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "people": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 12++ },++ "ai_run_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "recipes": {++ "type": "array",++ "maxItems": 3,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "title",++ "ingredients",++ "steps",++ "to_buy",++ "to_buy_cost",++ "cost_note"++ ],++ "properties": {++ "title": {++ "type": "string",++ "maxLength": 160++ },++ "serves": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 12++ },++ "minutes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 600++ },++ "ingredients": {++ "type": "array",++ "minItems": 1,++ "maxItems": 25,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "name",++ "amount",++ "from_pantry",++ "citations"++ ],++ "properties": {++ "name": {++ "type": "string",++ "maxLength": 120++ },++ "amount": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80++ },++ "from_pantry": {++ "type": "boolean"++ },++ "citations": {++ "type": "array",++ "items": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ "steps": {++ "type": "array",++ "minItems": 1,++ "maxItems": 20,++ "items": {++ "type": "string",++ "maxLength": 600++ }++ },++ "notes": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 600++ },++ "to_buy": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "slug",++ "name",++ "price"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "price": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 0++ }++ }++ }++ },++ "to_buy_cost": {++ "type": "number",++ "minimum": 0++ },++ "to_buy_cost_partial": {++ "type": "boolean"++ },++ "cost_note": {++ "type": "string"++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,39 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/work/application-result.v1.json",++ "title": "WorkApplicationResult",++ "description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",++ "anyOf": [++ {++ "$ref": "application.v1.json"++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "applications",++ "next",++ "count"++ ],++ "properties": {++ "applications": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "$ref": "application.v1.json"++ }++ },++ "next": {++ "type": [++ "string",++ "null"++ ]++ },++ "count": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,63 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/work/application-write-request.v1.json",++ "title": "WorkApplicationWriteRequest",++ "description": "work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.",++ "type": "object",++ "additionalProperties": false,++ "minProperties": 1,++ "properties": {++ "listing_id": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ },++ "company": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ },++ "status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "notes": {++ "type": "string",++ "maxLength": 5000++ },++ "applied_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "next_step": {++ "type": "string",++ "maxLength": 200++ },++ "next_step_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++}nnew file mode 100644@@@ -0,0 +1,148 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/work/application.v1.json",++ "title": "WorkApplication",++ "description": "work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "subject_id",++ "listing_id",++ "title",++ "company",++ "url",++ "status",++ "notes",++ "applied_at",++ "next_step",++ "next_step_at",++ "actor_task_id",++ "assists",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^wap_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "subject_id": {++ "type": "string",++ "pattern": "^user:usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "listing_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200,++ "description": "The Work listing it was tracked from, or null when added by hand."++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ },++ "company": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ },++ "status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "notes": {++ "type": "string",++ "maxLength": 5000++ },++ "applied_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "next_step": {++ "type": "string",++ "maxLength": 200++ },++ "next_step_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "actor_task_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200,++ "description": "The latest OpenVibe.Actor task asked for this application (POST …/assist)."++ },++ "assists": {++ "type": "integer",++ "minimum": 0++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "events": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "at",++ "from_status",++ "to_status"++ ],++ "properties": {++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "from_status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "to_status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ }++ }++ }++ }++ }++}nnew file mode 100644@@@ -0,0 +1,47 @@++[++ {++ "id": "rcp_01JQ8Z0000000000000000000C",++ "created_at": "2026-10-12T05:25:14.000Z",++ "ask": "something quick",++ "people": 2,++ "ai_run_id": "run_01JQ8Z0000000000000000000D",++ "recipes": [++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ }++ ]++ }++]nnew file mode 100644@@@ -0,0 +1,49 @@++{++ "recipes": [++ {++ "id": "rcp_01JQ8Z0000000000000000000C",++ "created_at": "2026-10-12T05:25:14.000Z",++ "ask": "something quick",++ "people": 2,++ "ai_run_id": "run_01JQ8Z0000000000000000000D",++ "recipes": [++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ }++ ]++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "people": 40++}nnew file mode 100644@@@ -0,0 +1,4 @@++{++ "ask": "something quick and filling",++ "people": 2++}nnew file mode 100644@@@ -0,0 +1 @@++{}nnew file mode 100644@@@ -0,0 +1,153 @@++{++ "id": "rcp_01JQ8Z0000000000000000000C",++ "created_at": "2026-10-12T05:25:14.000Z",++ "ask": "something quick",++ "people": 2,++ "ai_run_id": "run_01JQ8Z0000000000000000000D",++ "recipes": [++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ },++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ },++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ },++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,45 @@++{++ "id": "rcp_01JQ8Z0000000000000000000C",++ "created_at": "2026-10-12T05:25:14.000Z",++ "ask": "something quick",++ "people": 2,++ "ai_run_id": "run_01JQ8Z0000000000000000000D",++ "recipes": [++ {++ "title": "Egg fried rice",++ "serves": 2,++ "minutes": 15,++ "ingredients": [++ {++ "name": "Eggs",++ "amount": "3",++ "from_pantry": true,++ "citations": []++ },++ {++ "name": "White rice",++ "amount": "2 cups cooked",++ "from_pantry": false,++ "citations": [++ 1++ ]++ }++ ],++ "steps": [++ "Scramble the eggs and set them aside.",++ "Fry the rice, stir the eggs back in and serve."++ ],++ "notes": null,++ "to_buy": [++ {++ "slug": "rice-long-grain",++ "name": "Long Grain White Rice (2 lb)",++ "price": 1.25++ }++ ],++ "to_buy_cost": 1.25,++ "to_buy_cost_partial": false,++ "cost_note": "Cost is an estimate from typical prices, not a live price."++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,22 @@++{++ "applications": [++ {++ "id": "wap_01JQ8Z0000000000000000000A",++ "subject_id": "user:usr_01JQ8Z0000000000000000000B",++ "listing_id": null,++ "title": "Backend Engineer",++ "company": "Northwind",++ "url": "https://northwind.example/jobs/1",++ "status": "applied",++ "notes": "",++ "applied_at": "2026-10-12T05:00:00.000Z",++ "next_step": "",++ "next_step_at": null,++ "actor_task_id": null,++ "assists": 0,++ "created_at": "2026-10-12T05:00:00.000Z",++ "updated_at": "2026-10-12T05:00:00.000Z"++ }++ ],++ "next": null++}nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "applications": [++ {++ "id": "wap_01JQ8Z0000000000000000000A",++ "subject_id": "user:usr_01JQ8Z0000000000000000000B",++ "listing_id": null,++ "title": "Backend Engineer",++ "company": "Northwind",++ "url": "https://northwind.example/jobs/1",++ "status": "applied",++ "notes": "",++ "applied_at": "2026-10-12T05:00:00.000Z",++ "next_step": "",++ "next_step_at": null,++ "actor_task_id": null,++ "assists": 0,++ "created_at": "2026-10-12T05:00:00.000Z",++ "updated_at": "2026-10-12T05:00:00.000Z"++ }++ ],++ "next": null,++ "count": 1++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "salary": 100000++}nnew file mode 100644@@@ -0,0 +1,3 @@++{++ "listing_id": "job_arbeitnow_backend-engineer-1"++}nnew file mode 100644@@@ -0,0 +1,5 @@++{++ "status": "interviewing",++ "next_step": "Second interview",++ "next_step_at": "2026-10-16T17:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "wap_01JQ8Z0000000000000000000A",++ "subject_id": "user:usr_01JQ8Z0000000000000000000B",++ "listing_id": null,++ "title": "Backend Engineer",++ "company": "Northwind",++ "url": "https://northwind.example/jobs/1",++ "status": "ghosted",++ "notes": "",++ "applied_at": "2026-10-12T05:00:00.000Z",++ "next_step": "",++ "next_step_at": null,++ "actor_task_id": null,++ "assists": 0,++ "created_at": "2026-10-12T05:00:00.000Z",++ "updated_at": "2026-10-12T05:00:00.000Z"++}nnew file mode 100644@@@ -0,0 +1,17 @@++{++ "id": "wap_01JQ8Z0000000000000000000A",++ "subject_id": "user:usr_01JQ8Z0000000000000000000B",++ "listing_id": null,++ "title": "Backend Engineer",++ "company": "Northwind",++ "url": "https://northwind.example/jobs/1",++ "status": "applied",++ "notes": "",++ "applied_at": "2026-10-12T05:00:00.000Z",++ "next_step": "",++ "next_step_at": null,++ "actor_task_id": null,++ "assists": 0,++ "created_at": "2026-10-12T05:00:00.000Z",++ "updated_at": "2026-10-12T05:00:00.000Z"++}@@@ -76499,6 +76499,508 @@}}}++ },++ "work.application": {++ "version": "1.0.0",++ "owner": "work",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/work/application.v1.json",++ "title": "WorkApplication",++ "description": "work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "subject_id",++ "listing_id",++ "title",++ "company",++ "url",++ "status",++ "notes",++ "applied_at",++ "next_step",++ "next_step_at",++ "actor_task_id",++ "assists",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^wap_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "subject_id": {++ "type": "string",++ "pattern": "^user:usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "listing_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200,++ "description": "The Work listing it was tracked from, or null when added by hand."++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ },++ "company": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ },++ "status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "notes": {++ "type": "string",++ "maxLength": 5000++ },++ "applied_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "next_step": {++ "type": "string",++ "maxLength": 200++ },++ "next_step_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "actor_task_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200,++ "description": "The latest OpenVibe.Actor task asked for this application (POST …/assist)."++ },++ "assists": {++ "type": "integer",++ "minimum": 0++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "events": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "at",++ "from_status",++ "to_status"++ ],++ "properties": {++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "from_status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "to_status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ }++ }++ }++ }++ }++ }++ },++ "work.application-write-request": {++ "version": "1.0.0",++ "owner": "work",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/work/application-write-request.v1.json",++ "title": "WorkApplicationWriteRequest",++ "description": "work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.",++ "type": "object",++ "additionalProperties": false,++ "minProperties": 1,++ "properties": {++ "listing_id": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ },++ "company": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ },++ "status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "notes": {++ "type": "string",++ "maxLength": 5000++ },++ "applied_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "next_step": {++ "type": "string",++ "maxLength": 200++ },++ "next_step_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ }++ },++ "work.application-result": {++ "version": "1.0.0",++ "owner": "work",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/work/application-result.v1.json",++ "title": "WorkApplicationResult",++ "description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",++ "anyOf": [++ {++ "$ref": "application.v1.json"++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "applications",++ "next",++ "count"++ ],++ "properties": {++ "applications": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "$ref": "application.v1.json"++ }++ },++ "next": {++ "type": [++ "string",++ "null"++ ]++ },++ "count": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ ]++ }++ },++ "food.recipe-suggest-request": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/recipe-suggest-request.v1.json",++ "title": "FoodRecipeSuggestRequest",++ "description": "food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "ask": {++ "type": "string",++ "maxLength": 500++ },++ "people": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ }++ }++ }++ },++ "food.recipe-suggestion": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/recipe-suggestion.v1.json",++ "title": "FoodRecipeSuggestion",++ "description": "food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "created_at",++ "ask",++ "people",++ "ai_run_id",++ "recipes"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^rcp_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "ask": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "people": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 12++ },++ "ai_run_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "recipes": {++ "type": "array",++ "maxItems": 3,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "title",++ "ingredients",++ "steps",++ "to_buy",++ "to_buy_cost",++ "cost_note"++ ],++ "properties": {++ "title": {++ "type": "string",++ "maxLength": 160++ },++ "serves": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 12++ },++ "minutes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 600++ },++ "ingredients": {++ "type": "array",++ "minItems": 1,++ "maxItems": 25,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "name",++ "amount",++ "from_pantry",++ "citations"++ ],++ "properties": {++ "name": {++ "type": "string",++ "maxLength": 120++ },++ "amount": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80++ },++ "from_pantry": {++ "type": "boolean"++ },++ "citations": {++ "type": "array",++ "items": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ "steps": {++ "type": "array",++ "minItems": 1,++ "maxItems": 20,++ "items": {++ "type": "string",++ "maxLength": 600++ }++ },++ "notes": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 600++ },++ "to_buy": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "slug",++ "name",++ "price"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "price": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 0++ }++ }++ }++ },++ "to_buy_cost": {++ "type": "number",++ "minimum": 0++ },++ "to_buy_cost_partial": {++ "type": "boolean"++ },++ "cost_note": {++ "type": "string"++ }++ }++ }++ }++ }++ }++ },++ "food.recipe-result": {++ "version": "1.0.0",++ "owner": "food",++ "status": "active",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/food/recipe-result.v1.json",++ "title": "FoodRecipeResult",++ "description": "food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).",++ "anyOf": [++ {++ "$ref": "recipe-suggestion.v1.json"++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "recipes"++ ],++ "properties": {++ "recipes": {++ "type": "array",++ "maxItems": 20,++ "items": {++ "$ref": "recipe-suggestion.v1.json"++ }++ }++ }++ }++ ]++ }}}}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Food API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -271,6 +271,132 @@}}}++ },++ "/api/v1/recipes": {++ "get": {++ "operationId": "get_api_v1_recipes",++ "summary": "Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to ",++ "description": "**food.recipes.suggest** (public): Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to Food's food list; what is still to buy priced from Food's own typical prices), 10 a person a day; GET /api/v1/recipes lists the last 20. A person acting for themself needs no capability; an app or agent token needs this one.",++ "tags": [++ "food.recipes"++ ],++ "x-openvibe-capabilities": [++ "food.recipes.suggest"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "food.recipes.suggest"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/food.recipe-suggest-request.v1"++ },++ "parameters": [++ {++ "name": "ask",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 500++ }++ },++ {++ "name": "people",++ "in": "query",++ "required": false,++ "schema": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.recipe-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/recipes/suggest": {++ "post": {++ "operationId": "post_api_v1_recipes_suggest",++ "summary": "Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to ",++ "description": "**food.recipes.suggest** (public): Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to Food's food list; what is still to buy priced from Food's own typical prices), 10 a person a day; GET /api/v1/recipes lists the last 20. A person acting for themself needs no capability; an app or agent token needs this one.",++ "tags": [++ "food.recipes"++ ],++ "x-openvibe-capabilities": [++ "food.recipes.suggest"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "food.recipes.suggest"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.recipe-suggest-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/food.recipe-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }}},"components": {@@@ -642,6 +768,222 @@}}}++ },++ "food.recipe-result.v1": {++ "title": "FoodRecipeResult",++ "description": "food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).",++ "anyOf": [++ {++ "$ref": "#/components/schemas/food.recipe-suggestion.v1"++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "recipes"++ ],++ "properties": {++ "recipes": {++ "type": "array",++ "maxItems": 20,++ "items": {++ "$ref": "#/components/schemas/food.recipe-suggestion.v1"++ }++ }++ }++ }++ ]++ },++ "food.recipe-suggest-request.v1": {++ "title": "FoodRecipeSuggestRequest",++ "description": "food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.",++ "type": "object",++ "additionalProperties": false,++ "properties": {++ "ask": {++ "type": "string",++ "maxLength": 500++ },++ "people": {++ "type": "integer",++ "minimum": 1,++ "maximum": 12++ }++ }++ },++ "food.recipe-suggestion.v1": {++ "title": "FoodRecipeSuggestion",++ "description": "food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "created_at",++ "ask",++ "people",++ "ai_run_id",++ "recipes"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^rcp_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "ask": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 500++ },++ "people": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 12++ },++ "ai_run_id": {++ "type": [++ "string",++ "null"++ ]++ },++ "recipes": {++ "type": "array",++ "maxItems": 3,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "title",++ "ingredients",++ "steps",++ "to_buy",++ "to_buy_cost",++ "cost_note"++ ],++ "properties": {++ "title": {++ "type": "string",++ "maxLength": 160++ },++ "serves": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 12++ },++ "minutes": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "maximum": 600++ },++ "ingredients": {++ "type": "array",++ "minItems": 1,++ "maxItems": 25,++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "name",++ "amount",++ "from_pantry",++ "citations"++ ],++ "properties": {++ "name": {++ "type": "string",++ "maxLength": 120++ },++ "amount": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 80++ },++ "from_pantry": {++ "type": "boolean"++ },++ "citations": {++ "type": "array",++ "items": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ }++ },++ "steps": {++ "type": "array",++ "minItems": 1,++ "maxItems": 20,++ "items": {++ "type": "string",++ "maxLength": 600++ }++ },++ "notes": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 600++ },++ "to_buy": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "slug",++ "name",++ "price"++ ],++ "properties": {++ "slug": {++ "type": "string"++ },++ "name": {++ "type": "string"++ },++ "price": {++ "type": [++ "number",++ "null"++ ],++ "minimum": 0++ }++ }++ }++ },++ "to_buy_cost": {++ "type": "number",++ "minimum": 0++ },++ "to_buy_cost_partial": {++ "type": "boolean"++ },++ "cost_note": {++ "type": "string"++ }++ }++ }++ }++ }}},"securitySchemes": {@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -75,8 +75,8 @@"service": "food","name": "OpenVibe.Food","origin": "https://openvibe.food",-- "operations": 5,-- "capabilities": 4++ "operations": 7,++ "capabilities": 5},{"service": "games",@@@ -210,6 +210,13 @@"origin": "https://openvibe.wiki","operations": 25,"capabilities": 8++ },++ {++ "service": "work",++ "name": "OpenVibe.Work",++ "origin": "https://openvibe.work",++ "operations": 5,++ "capabilities": 1}]}@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRestream API",-- "version": "0.146.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.146.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"nnew file mode 100644@@@ -0,0 +1,918 @@++{++ "openapi": "3.1.0",++ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",++ "info": {++ "title": "OpenVibe.Work API",++ "version": "0.147.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "license": {++ "name": "MIT",++ "identifier": "MIT"++ }++ },++ "servers": [++ {++ "url": "https://openvibe.work"++ }++ ],++ "paths": {++ "/api/v1/me/applications": {++ "get": {++ "operationId": "get_api_v1_me_applications",++ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",++ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",++ "tags": [++ "work.application"++ ],++ "x-openvibe-capabilities": [++ "work.application.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "work.application.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/work.application-write-request.v1"++ },++ "parameters": [++ {++ "name": "listing_id",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ }++ },++ {++ "name": "title",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ }++ },++ {++ "name": "company",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ }++ },++ {++ "name": "url",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ }++ },++ {++ "name": "status",++ "in": "query",++ "required": false,++ "schema": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ }++ },++ {++ "name": "notes",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 5000++ }++ },++ {++ "name": "applied_at",++ "in": "query",++ "required": false,++ "schema": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ },++ {++ "name": "next_step",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 200++ }++ },++ {++ "name": "next_step_at",++ "in": "query",++ "required": false,++ "schema": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "post": {++ "operationId": "post_api_v1_me_applications",++ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",++ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",++ "tags": [++ "work.application"++ ],++ "x-openvibe-capabilities": [++ "work.application.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "work.application.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-write-request.v1"++ }++ }++ }++ },++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ },++ "/api/v1/me/applications/{id}": {++ "delete": {++ "operationId": "delete_api_v1_me_applications_id",++ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",++ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",++ "tags": [++ "work.application"++ ],++ "x-openvibe-capabilities": [++ "work.application.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "work.application.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/work.application-write-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "listing_id",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ }++ },++ {++ "name": "title",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ }++ },++ {++ "name": "company",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ }++ },++ {++ "name": "url",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ }++ },++ {++ "name": "status",++ "in": "query",++ "required": false,++ "schema": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ }++ },++ {++ "name": "notes",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 5000++ }++ },++ {++ "name": "applied_at",++ "in": "query",++ "required": false,++ "schema": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ },++ {++ "name": "next_step",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 200++ }++ },++ {++ "name": "next_step_at",++ "in": "query",++ "required": false,++ "schema": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "get": {++ "operationId": "get_api_v1_me_applications_id",++ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",++ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",++ "tags": [++ "work.application"++ ],++ "x-openvibe-capabilities": [++ "work.application.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "work.application.manage"++ ]++ }++ ],++ "x-openvibe-input": {++ "$ref": "#/components/schemas/work.application-write-request.v1"++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ },++ {++ "name": "listing_id",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ }++ },++ {++ "name": "title",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ }++ },++ {++ "name": "company",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ }++ },++ {++ "name": "url",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ }++ },++ {++ "name": "status",++ "in": "query",++ "required": false,++ "schema": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ }++ },++ {++ "name": "notes",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 5000++ }++ },++ {++ "name": "applied_at",++ "in": "query",++ "required": false,++ "schema": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ },++ {++ "name": "next_step",++ "in": "query",++ "required": false,++ "schema": {++ "type": "string",++ "maxLength": 200++ }++ },++ {++ "name": "next_step_at",++ "in": "query",++ "required": false,++ "schema": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ },++ "patch": {++ "operationId": "patch_api_v1_me_applications_id",++ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",++ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",++ "tags": [++ "work.application"++ ],++ "x-openvibe-capabilities": [++ "work.application.manage"++ ],++ "x-openvibe-visibility": [++ "public"++ ],++ "x-openvibe-quota-class": "standard",++ "security": [++ {++ "openvibeToken": [++ "work.application.manage"++ ]++ }++ ],++ "requestBody": {++ "required": false,++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-write-request.v1"++ }++ }++ }++ },++ "parameters": [++ {++ "name": "id",++ "in": "path",++ "required": true,++ "schema": {++ "type": "string"++ }++ }++ ],++ "responses": {++ "2XX": {++ "description": "Success",++ "content": {++ "application/json": {++ "schema": {++ "$ref": "#/components/schemas/work.application-result.v1"++ }++ }++ }++ },++ "default": {++ "description": "An error (RFC 9457 problem details)",++ "content": {++ "application/problem+json": {++ "schema": {++ "$ref": "#/components/schemas/errors.problem.v1"++ }++ }++ }++ }++ }++ }++ }++ },++ "components": {++ "schemas": {++ "errors.problem.v1": {++ "title": "Problem",++ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",++ "type": "object",++ "required": [++ "type",++ "title",++ "status",++ "code"++ ],++ "additionalProperties": true,++ "properties": {++ "type": {++ "type": "string",++ "format": "uri-reference"++ },++ "title": {++ "type": "string",++ "minLength": 1++ },++ "status": {++ "type": "integer",++ "minimum": 400,++ "maximum": 599++ },++ "detail": {++ "type": "string"++ },++ "instance": {++ "type": "string",++ "format": "uri-reference"++ },++ "code": {++ "type": "string",++ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"++ },++ "request_id": {++ "type": "string"++ },++ "trace_id": {++ "type": "string",++ "pattern": "^[0-9a-f]{32}$"++ },++ "errors": {++ "type": "array",++ "items": {++ "type": "object",++ "required": [++ "message"++ ],++ "properties": {++ "path": {++ "type": "string"++ },++ "message": {++ "type": "string"++ }++ }++ }++ },++ "error": {++ "type": "string",++ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."++ }++ }++ },++ "work.application-result.v1": {++ "title": "WorkApplicationResult",++ "description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",++ "anyOf": [++ {++ "$ref": "#/components/schemas/work.application.v1"++ },++ {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "applications",++ "next",++ "count"++ ],++ "properties": {++ "applications": {++ "type": "array",++ "maxItems": 100,++ "items": {++ "$ref": "#/components/schemas/work.application.v1"++ }++ },++ "next": {++ "type": [++ "string",++ "null"++ ]++ },++ "count": {++ "type": "integer",++ "minimum": 0++ }++ }++ }++ ]++ },++ "work.application-write-request.v1": {++ "title": "WorkApplicationWriteRequest",++ "description": "work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.",++ "type": "object",++ "additionalProperties": false,++ "minProperties": 1,++ "properties": {++ "listing_id": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ },++ "company": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ },++ "status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "notes": {++ "type": "string",++ "maxLength": 5000++ },++ "applied_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "next_step": {++ "type": "string",++ "maxLength": 200++ },++ "next_step_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ }++ }++ },++ "work.application.v1": {++ "title": "WorkApplication",++ "description": "work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "subject_id",++ "listing_id",++ "title",++ "company",++ "url",++ "status",++ "notes",++ "applied_at",++ "next_step",++ "next_step_at",++ "actor_task_id",++ "assists",++ "created_at",++ "updated_at"++ ],++ "properties": {++ "id": {++ "type": "string",++ "pattern": "^wap_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "subject_id": {++ "type": "string",++ "pattern": "^user:usr_[0-9A-HJKMNP-TV-Z]{26}$"++ },++ "listing_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200,++ "description": "The Work listing it was tracked from, or null when added by hand."++ },++ "title": {++ "type": "string",++ "minLength": 1,++ "maxLength": 500++ },++ "company": {++ "type": "string",++ "minLength": 1,++ "maxLength": 200++ },++ "url": {++ "type": "string",++ "format": "uri",++ "maxLength": 2000++ },++ "status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "notes": {++ "type": "string",++ "maxLength": 5000++ },++ "applied_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "next_step": {++ "type": "string",++ "maxLength": 200++ },++ "next_step_at": {++ "type": [++ "string",++ "null"++ ],++ "format": "date-time"++ },++ "actor_task_id": {++ "type": [++ "string",++ "null"++ ],++ "maxLength": 200,++ "description": "The latest OpenVibe.Actor task asked for this application (POST …/assist)."++ },++ "assists": {++ "type": "integer",++ "minimum": 0++ },++ "created_at": {++ "type": "string",++ "format": "date-time"++ },++ "updated_at": {++ "type": "string",++ "format": "date-time"++ },++ "events": {++ "type": "array",++ "items": {++ "type": "object",++ "additionalProperties": false,++ "required": [++ "at",++ "from_status",++ "to_status"++ ],++ "properties": {++ "at": {++ "type": "string",++ "format": "date-time"++ },++ "from_status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ },++ "to_status": {++ "enum": [++ "saved",++ "applied",++ "interviewing",++ "offer",++ "rejected",++ "withdrawn"++ ]++ }++ }++ }++ }++ }++ }++ },++ "securitySchemes": {++ "openvibeToken": {++ "type": "http",++ "scheme": "bearer",++ "bearerFormat": "JWT",++ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."++ }++ }++ }++}@@@ -171633,3 +171633,1441 @@ export interface QuestIndexDocumentDeletedPayload {*/revision: number;}++++/** [email protected] (owner: work) */++/**++ * work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.++ */++export interface WorkApplication {++ id: string;++ subject_id: string;++ /**++ * The Work listing it was tracked from, or null when added by hand.++ */++ listing_id: string | null;++ title: string;++ company: string;++ url: string;++ status: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";++ notes: string;++ applied_at: string | null;++ next_step: string;++ next_step_at: string | null;++ /**++ * The latest OpenVibe.Actor task asked for this application (POST …/assist).++ */++ actor_task_id: string | null;++ assists: number;++ created_at: string;++ updated_at: string;++ events?: {++ at: string;++ from_status: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";++ to_status: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";++ }[];++}++++/** [email protected] (owner: work) */++/**++ * work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.++ */++export interface WorkApplicationWriteRequest {++ listing_id?: string;++ title?: string;++ company?: string;++ url?: string;++ status?: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";++ notes?: string;++ applied_at?: string | null;++ next_step?: string;++ next_step_at?: string | null;++}++++/** [email protected] (owner: work) */++/**++ * work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.++ */++export type WorkApplicationResult =++ | WorkApplication++ | {++ /**++ * @maxItems 100++ */++ applications: WorkApplication[];++ next: string | null;++ count: number;++ };++++/** [email protected] (owner: food) */++/**++ * food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.++ */++export interface FoodRecipeSuggestRequest {++ ask?: string;++ people?: number;++}++++/** [email protected] (owner: food) */++/**++ * food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.++ */++export interface FoodRecipeSuggestion {++ id: string;++ created_at: string;++ ask: string | null;++ people: number | null;++ ai_run_id: string | null;++ /**++ * @maxItems 3++ */++ recipes:++ | []++ | [++ {++ title: string;++ serves?: number | null;++ minutes?: number | null;++ /**++ * @minItems 1++ * @maxItems 25++ */++ ingredients: [++ {++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ },++ ...{++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ }[]++ ];++ /**++ * @minItems 1++ * @maxItems 20++ */++ steps:++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string, string]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ notes?: string | null;++ to_buy: {++ slug: string;++ name: string;++ price: number | null;++ }[];++ to_buy_cost: number;++ to_buy_cost_partial?: boolean;++ cost_note: string;++ }++ ]++ | [++ {++ title: string;++ serves?: number | null;++ minutes?: number | null;++ /**++ * @minItems 1++ * @maxItems 25++ */++ ingredients: [++ {++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ },++ ...{++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ }[]++ ];++ /**++ * @minItems 1++ * @maxItems 20++ */++ steps:++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string, string]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ notes?: string | null;++ to_buy: {++ slug: string;++ name: string;++ price: number | null;++ }[];++ to_buy_cost: number;++ to_buy_cost_partial?: boolean;++ cost_note: string;++ },++ {++ title: string;++ serves?: number | null;++ minutes?: number | null;++ /**++ * @minItems 1++ * @maxItems 25++ */++ ingredients: [++ {++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ },++ ...{++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ }[]++ ];++ /**++ * @minItems 1++ * @maxItems 20++ */++ steps:++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string, string]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ notes?: string | null;++ to_buy: {++ slug: string;++ name: string;++ price: number | null;++ }[];++ to_buy_cost: number;++ to_buy_cost_partial?: boolean;++ cost_note: string;++ }++ ]++ | [++ {++ title: string;++ serves?: number | null;++ minutes?: number | null;++ /**++ * @minItems 1++ * @maxItems 25++ */++ ingredients: [++ {++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ },++ ...{++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ }[]++ ];++ /**++ * @minItems 1++ * @maxItems 20++ */++ steps:++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string, string]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ notes?: string | null;++ to_buy: {++ slug: string;++ name: string;++ price: number | null;++ }[];++ to_buy_cost: number;++ to_buy_cost_partial?: boolean;++ cost_note: string;++ },++ {++ title: string;++ serves?: number | null;++ minutes?: number | null;++ /**++ * @minItems 1++ * @maxItems 25++ */++ ingredients: [++ {++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ },++ ...{++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ }[]++ ];++ /**++ * @minItems 1++ * @maxItems 20++ */++ steps:++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string, string]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ notes?: string | null;++ to_buy: {++ slug: string;++ name: string;++ price: number | null;++ }[];++ to_buy_cost: number;++ to_buy_cost_partial?: boolean;++ cost_note: string;++ },++ {++ title: string;++ serves?: number | null;++ minutes?: number | null;++ /**++ * @minItems 1++ * @maxItems 25++ */++ ingredients: [++ {++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ },++ ...{++ name: string;++ amount: string | null;++ from_pantry: boolean;++ citations: number[];++ }[]++ ];++ /**++ * @minItems 1++ * @maxItems 20++ */++ steps:++ | [string]++ | [string, string]++ | [string, string, string]++ | [string, string, string, string]++ | [string, string, string, string, string]++ | [string, string, string, string, string, string]++ | [string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string]++ | [string, string, string, string, string, string, string, string, string, string, string, string, string]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ]++ | [++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string,++ string++ ];++ notes?: string | null;++ to_buy: {++ slug: string;++ name: string;++ price: number | null;++ }[];++ to_buy_cost: number;++ to_buy_cost_partial?: boolean;++ cost_note: string;++ }++ ];++}++++/** [email protected] (owner: food) */++/**++ * food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).++ */++export type FoodRecipeResult =++ | FoodRecipeSuggestion++ | {++ /**++ * @maxItems 20++ */++ recipes:++ | []++ | [FoodRecipeSuggestion]++ | [FoodRecipeSuggestion, FoodRecipeSuggestion]++ | [FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion]++ | [FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion]++ | [FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ]++ | [++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion,++ FoodRecipeSuggestion++ ];++ };nnew file mode 100644@@@ -0,0 +1,23 @@++{++ "id": "food.recipes.suggest",++ "version": "1.0",++ "owner": "food",++ "status": "active",++ "visibility": "public",++ "sensitive": true,++ "description": "Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to Food's food list; what is still to buy priced from Food's own typical prices), 10 a person a day; GET /api/v1/recipes lists the last 20. A person acting for themself needs no capability; an app or agent token needs this one.",++ "inputSchema": "food.recipe-suggest-request@1",++ "outputSchema": "food.recipe-result@1",++ "permissions": [++ "food:recipes:suggest"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "POST /api/v1/recipes/suggest",++ "GET /api/v1/recipes"++ ]++}nnew file mode 100644@@@ -0,0 +1,26 @@++{++ "id": "work.application.manage",++ "version": "1.0",++ "owner": "work",++ "status": "active",++ "visibility": "public",++ "sensitive": true,++ "description": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",++ "inputSchema": "work.application-write-request@1",++ "outputSchema": "work.application-result@1",++ "permissions": [++ "work:application:manage"++ ],++ "resourceConstraints": [++ "owner"++ ],++ "quotaClass": "standard",++ "events": [],++ "implementedBy": [++ "GET /api/v1/me/applications",++ "POST /api/v1/me/applications",++ "GET /api/v1/me/applications/:id",++ "PATCH /api/v1/me/applications/:id",++ "DELETE /api/v1/me/applications/:id"++ ]++}@@@ -19,7 +19,8 @@"food.plan.read","food.plan.write","food.pantry.read",-- "food.pantry.write"++ "food.pantry.write",++ "food.recipes.suggest"],"eventsProduced": [],"eventsConsumed": [@@@ -15,7 +15,9 @@"state": "live","publicSite": "service"},-- "capabilities": [],++ "capabilities": [++ "work.application.manage"++ ],"eventsProduced": ["work.index_document.upserted","work.index_document.deleted"@@@ -71,7 +73,7 @@"none": "no claims: one process serves the site"}},-- "notes": "Port 4960, unit openvibe-work.service. Job listings gathered from open job-board APIs (Arbeitnow, Remotive, RemoteOK) with their provenance: a short plain-text excerpt only, the source named and the original listing linked on every list and page, never the full description. Search with remote/type/location filters, JobPosting JSON-LD, and saved searches with a count of what is new since you last looked. The ingest runs every 6 hours per source when WORK_INGEST=on, upserts by (source, source_id) and expires listings not seen for 30 days. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client work, JWKS). Owns work_listings, work_source_fetches and work_saved_searches. No capabilities yet: saved searches are a person's, listings are public.",++ "notes": "Port 4960, unit openvibe-work.service. Job listings gathered from open job-board APIs (Arbeitnow, Remotive, RemoteOK) with their provenance: a short plain-text excerpt only, the source named and the original listing linked on every list and page, never the full description. Search with remote/type/location filters, JobPosting JSON-LD, and saved searches with a count of what is new since you last looked. The ingest runs every 6 hours per source when WORK_INGEST=on, upserts by (source, source_id) and expires listings not seen for 30 days. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client work, JWKS). Owns work_listings, work_source_fetches, work_saved_searches, work_applications and work_application_events. The agent pillar: a person tracks applications (status, notes, next steps, history; work.application.manage for apps and agents) and can ask their OpenVibe.Actor agent to help prepare one (actor.task.create with X-OV-Subject, idempotency key per assist, 10 a day).","health": "/api/health","ready": "/api/ready","internalOrigin": "http://127.0.0.1:4960",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.146.0",++ "version": "0.147.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",
@@ -4,6 +4,15 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,15 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.147.0 — 2026-10-12
+
+- `work.application.manage` (owner work): a person's tracked job applications on OpenVibe.Work, for apps and agents
+ (`work.application@1`, `work.application-write-request@1`, `work.application-result@1`). Asking the person's
+ OpenVibe.Actor agent about one is the person's own act and takes no capability. The work manifest lists it.
+- `food.recipes.suggest` (owner food): recipe ideas from the caller's pantry through OpenVibe.AI's
+ `food.suggest_recipes`, priced from Food's own food list (`food.recipe-suggest-request@1`,
+ `food.recipe-suggestion@1`, `food.recipe-result@1`). The food manifest lists it.
+
## 0.146.0 — 2026-10-12
## 0.146.0 — 2026-10-12
- `sources.source@1` and `sources.source-write-request@1`: an `api` endpoint may declare `where` (up to 10 path →
- `sources.source@1` and `sources.source-write-request@1`: an `api` endpoint may declare `where` (up to 10 path →
@@ -6802,6 +6802,60 @@
@@ -6802,6 +6802,60 @@
"visibility": "first-party",
"visibility": "first-party",
"compatibility": "backward",
"compatibility": "backward",
"status": "active"
"status": "active"
+ },
+ {
+ "id": "work.application",
+ "version": "1.0.0",
+ "owner": "work",
+ "schema": "work/application.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "work.application-write-request",
+ "version": "1.0.0",
+ "owner": "work",
+ "schema": "work/application-write-request.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "work.application-result",
+ "version": "1.0.0",
+ "owner": "work",
+ "schema": "work/application-result.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.recipe-suggest-request",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/recipe-suggest-request.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.recipe-suggestion",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/recipe-suggestion.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
+ },
+ {
+ "id": "food.recipe-result",
+ "version": "1.0.0",
+ "owner": "food",
+ "schema": "food/recipe-result.v1.json",
+ "visibility": "public",
+ "compatibility": "backward",
+ "status": "active"
}
}
]
]
}
}
new file mode 100644
new file mode 100644
@@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/recipe-result.v1.json",
+ "title": "FoodRecipeResult",
+ "description": "food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).",
+ "anyOf": [
+ {
+ "$ref": "recipe-suggestion.v1.json"
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "recipes"
+ ],
+ "properties": {
+ "recipes": {
+ "type": "array",
+ "maxItems": 20,
+ "items": {
+ "$ref": "recipe-suggestion.v1.json"
+ }
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,19 @@
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/recipe-suggest-request.v1.json",
+ "title": "FoodRecipeSuggestRequest",
+ "description": "food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "ask": {
+ "type": "string",
+ "maxLength": 500
+ },
+ "people": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,176 @@
@@ -0,0 +1,176 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/recipe-suggestion.v1.json",
+ "title": "FoodRecipeSuggestion",
+ "description": "food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "created_at",
+ "ask",
+ "people",
+ "ai_run_id",
+ "recipes"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^rcp_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "ask": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "people": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 12
+ },
+ "ai_run_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "recipes": {
+ "type": "array",
+ "maxItems": 3,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "title",
+ "ingredients",
+ "steps",
+ "to_buy",
+ "to_buy_cost",
+ "cost_note"
+ ],
+ "properties": {
+ "title": {
+ "type": "string",
+ "maxLength": 160
+ },
+ "serves": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 12
+ },
+ "minutes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 600
+ },
+ "ingredients": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 25,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "name",
+ "amount",
+ "from_pantry",
+ "citations"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "maxLength": 120
+ },
+ "amount": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80
+ },
+ "from_pantry": {
+ "type": "boolean"
+ },
+ "citations": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ "steps": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 20,
+ "items": {
+ "type": "string",
+ "maxLength": 600
+ }
+ },
+ "notes": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 600
+ },
+ "to_buy": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "slug",
+ "name",
+ "price"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "price": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0
+ }
+ }
+ }
+ },
+ "to_buy_cost": {
+ "type": "number",
+ "minimum": 0
+ },
+ "to_buy_cost_partial": {
+ "type": "boolean"
+ },
+ "cost_note": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/work/application-result.v1.json",
+ "title": "WorkApplicationResult",
+ "description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",
+ "anyOf": [
+ {
+ "$ref": "application.v1.json"
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "applications",
+ "next",
+ "count"
+ ],
+ "properties": {
+ "applications": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "$ref": "application.v1.json"
+ }
+ },
+ "next": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/work/application-write-request.v1.json",
+ "title": "WorkApplicationWriteRequest",
+ "description": "work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.",
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "listing_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ },
+ "status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "notes": {
+ "type": "string",
+ "maxLength": 5000
+ },
+ "applied_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "next_step": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "next_step_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/work/application.v1.json",
+ "title": "WorkApplication",
+ "description": "work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "subject_id",
+ "listing_id",
+ "title",
+ "company",
+ "url",
+ "status",
+ "notes",
+ "applied_at",
+ "next_step",
+ "next_step_at",
+ "actor_task_id",
+ "assists",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^wap_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "subject_id": {
+ "type": "string",
+ "pattern": "^user:usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "listing_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200,
+ "description": "The Work listing it was tracked from, or null when added by hand."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ },
+ "status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "notes": {
+ "type": "string",
+ "maxLength": 5000
+ },
+ "applied_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "next_step": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "next_step_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "actor_task_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200,
+ "description": "The latest OpenVibe.Actor task asked for this application (POST …/assist)."
+ },
+ "assists": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "events": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "at",
+ "from_status",
+ "to_status"
+ ],
+ "properties": {
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "from_status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "to_status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ }
+ }
+ }
+ }
+ }
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
+[
+ {
+ "id": "rcp_01JQ8Z0000000000000000000C",
+ "created_at": "2026-10-12T05:25:14.000Z",
+ "ask": "something quick",
+ "people": 2,
+ "ai_run_id": "run_01JQ8Z0000000000000000000D",
+ "recipes": [
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ }
+ ]
+ }
+]
new file mode 100644
new file mode 100644
@@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
+{
+ "recipes": [
+ {
+ "id": "rcp_01JQ8Z0000000000000000000C",
+ "created_at": "2026-10-12T05:25:14.000Z",
+ "ask": "something quick",
+ "people": 2,
+ "ai_run_id": "run_01JQ8Z0000000000000000000D",
+ "recipes": [
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ }
+ ]
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "people": 40
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
+{
+ "ask": "something quick and filling",
+ "people": 2
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{}
new file mode 100644
new file mode 100644
@@ -0,0 +1,153 @@
@@ -0,0 +1,153 @@
+{
+ "id": "rcp_01JQ8Z0000000000000000000C",
+ "created_at": "2026-10-12T05:25:14.000Z",
+ "ask": "something quick",
+ "people": 2,
+ "ai_run_id": "run_01JQ8Z0000000000000000000D",
+ "recipes": [
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ },
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ },
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ },
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
+{
+ "id": "rcp_01JQ8Z0000000000000000000C",
+ "created_at": "2026-10-12T05:25:14.000Z",
+ "ask": "something quick",
+ "people": 2,
+ "ai_run_id": "run_01JQ8Z0000000000000000000D",
+ "recipes": [
+ {
+ "title": "Egg fried rice",
+ "serves": 2,
+ "minutes": 15,
+ "ingredients": [
+ {
+ "name": "Eggs",
+ "amount": "3",
+ "from_pantry": true,
+ "citations": []
+ },
+ {
+ "name": "White rice",
+ "amount": "2 cups cooked",
+ "from_pantry": false,
+ "citations": [
+ 1
+ ]
+ }
+ ],
+ "steps": [
+ "Scramble the eggs and set them aside.",
+ "Fry the rice, stir the eggs back in and serve."
+ ],
+ "notes": null,
+ "to_buy": [
+ {
+ "slug": "rice-long-grain",
+ "name": "Long Grain White Rice (2 lb)",
+ "price": 1.25
+ }
+ ],
+ "to_buy_cost": 1.25,
+ "to_buy_cost_partial": false,
+ "cost_note": "Cost is an estimate from typical prices, not a live price."
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,22 @@
@@ -0,0 +1,22 @@
+{
+ "applications": [
+ {
+ "id": "wap_01JQ8Z0000000000000000000A",
+ "subject_id": "user:usr_01JQ8Z0000000000000000000B",
+ "listing_id": null,
+ "title": "Backend Engineer",
+ "company": "Northwind",
+ "url": "https://northwind.example/jobs/1",
+ "status": "applied",
+ "notes": "",
+ "applied_at": "2026-10-12T05:00:00.000Z",
+ "next_step": "",
+ "next_step_at": null,
+ "actor_task_id": null,
+ "assists": 0,
+ "created_at": "2026-10-12T05:00:00.000Z",
+ "updated_at": "2026-10-12T05:00:00.000Z"
+ }
+ ],
+ "next": null
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "applications": [
+ {
+ "id": "wap_01JQ8Z0000000000000000000A",
+ "subject_id": "user:usr_01JQ8Z0000000000000000000B",
+ "listing_id": null,
+ "title": "Backend Engineer",
+ "company": "Northwind",
+ "url": "https://northwind.example/jobs/1",
+ "status": "applied",
+ "notes": "",
+ "applied_at": "2026-10-12T05:00:00.000Z",
+ "next_step": "",
+ "next_step_at": null,
+ "actor_task_id": null,
+ "assists": 0,
+ "created_at": "2026-10-12T05:00:00.000Z",
+ "updated_at": "2026-10-12T05:00:00.000Z"
+ }
+ ],
+ "next": null,
+ "count": 1
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "salary": 100000
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,3 @@
@@ -0,0 +1,3 @@
+{
+ "listing_id": "job_arbeitnow_backend-engineer-1"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
+{
+ "status": "interviewing",
+ "next_step": "Second interview",
+ "next_step_at": "2026-10-16T17:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "wap_01JQ8Z0000000000000000000A",
+ "subject_id": "user:usr_01JQ8Z0000000000000000000B",
+ "listing_id": null,
+ "title": "Backend Engineer",
+ "company": "Northwind",
+ "url": "https://northwind.example/jobs/1",
+ "status": "ghosted",
+ "notes": "",
+ "applied_at": "2026-10-12T05:00:00.000Z",
+ "next_step": "",
+ "next_step_at": null,
+ "actor_task_id": null,
+ "assists": 0,
+ "created_at": "2026-10-12T05:00:00.000Z",
+ "updated_at": "2026-10-12T05:00:00.000Z"
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
+{
+ "id": "wap_01JQ8Z0000000000000000000A",
+ "subject_id": "user:usr_01JQ8Z0000000000000000000B",
+ "listing_id": null,
+ "title": "Backend Engineer",
+ "company": "Northwind",
+ "url": "https://northwind.example/jobs/1",
+ "status": "applied",
+ "notes": "",
+ "applied_at": "2026-10-12T05:00:00.000Z",
+ "next_step": "",
+ "next_step_at": null,
+ "actor_task_id": null,
+ "assists": 0,
+ "created_at": "2026-10-12T05:00:00.000Z",
+ "updated_at": "2026-10-12T05:00:00.000Z"
+}
@@ -76499,6 +76499,508 @@
@@ -76499,6 +76499,508 @@
}
}
}
}
}
}
+ },
+ "work.application": {
+ "version": "1.0.0",
+ "owner": "work",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/work/application.v1.json",
+ "title": "WorkApplication",
+ "description": "work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "subject_id",
+ "listing_id",
+ "title",
+ "company",
+ "url",
+ "status",
+ "notes",
+ "applied_at",
+ "next_step",
+ "next_step_at",
+ "actor_task_id",
+ "assists",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^wap_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "subject_id": {
+ "type": "string",
+ "pattern": "^user:usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "listing_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200,
+ "description": "The Work listing it was tracked from, or null when added by hand."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ },
+ "status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "notes": {
+ "type": "string",
+ "maxLength": 5000
+ },
+ "applied_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "next_step": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "next_step_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "actor_task_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200,
+ "description": "The latest OpenVibe.Actor task asked for this application (POST …/assist)."
+ },
+ "assists": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "events": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "at",
+ "from_status",
+ "to_status"
+ ],
+ "properties": {
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "from_status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "to_status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "work.application-write-request": {
+ "version": "1.0.0",
+ "owner": "work",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/work/application-write-request.v1.json",
+ "title": "WorkApplicationWriteRequest",
+ "description": "work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.",
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "listing_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ },
+ "status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "notes": {
+ "type": "string",
+ "maxLength": 5000
+ },
+ "applied_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "next_step": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "next_step_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ }
+ },
+ "work.application-result": {
+ "version": "1.0.0",
+ "owner": "work",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/work/application-result.v1.json",
+ "title": "WorkApplicationResult",
+ "description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",
+ "anyOf": [
+ {
+ "$ref": "application.v1.json"
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "applications",
+ "next",
+ "count"
+ ],
+ "properties": {
+ "applications": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "$ref": "application.v1.json"
+ }
+ },
+ "next": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ ]
+ }
+ },
+ "food.recipe-suggest-request": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/recipe-suggest-request.v1.json",
+ "title": "FoodRecipeSuggestRequest",
+ "description": "food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "ask": {
+ "type": "string",
+ "maxLength": 500
+ },
+ "people": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ }
+ }
+ }
+ },
+ "food.recipe-suggestion": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/recipe-suggestion.v1.json",
+ "title": "FoodRecipeSuggestion",
+ "description": "food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "created_at",
+ "ask",
+ "people",
+ "ai_run_id",
+ "recipes"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^rcp_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "ask": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "people": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 12
+ },
+ "ai_run_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "recipes": {
+ "type": "array",
+ "maxItems": 3,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "title",
+ "ingredients",
+ "steps",
+ "to_buy",
+ "to_buy_cost",
+ "cost_note"
+ ],
+ "properties": {
+ "title": {
+ "type": "string",
+ "maxLength": 160
+ },
+ "serves": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 12
+ },
+ "minutes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 600
+ },
+ "ingredients": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 25,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "name",
+ "amount",
+ "from_pantry",
+ "citations"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "maxLength": 120
+ },
+ "amount": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80
+ },
+ "from_pantry": {
+ "type": "boolean"
+ },
+ "citations": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ "steps": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 20,
+ "items": {
+ "type": "string",
+ "maxLength": 600
+ }
+ },
+ "notes": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 600
+ },
+ "to_buy": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "slug",
+ "name",
+ "price"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "price": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0
+ }
+ }
+ }
+ },
+ "to_buy_cost": {
+ "type": "number",
+ "minimum": 0
+ },
+ "to_buy_cost_partial": {
+ "type": "boolean"
+ },
+ "cost_note": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "food.recipe-result": {
+ "version": "1.0.0",
+ "owner": "food",
+ "status": "active",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/food/recipe-result.v1.json",
+ "title": "FoodRecipeResult",
+ "description": "food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).",
+ "anyOf": [
+ {
+ "$ref": "recipe-suggestion.v1.json"
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "recipes"
+ ],
+ "properties": {
+ "recipes": {
+ "type": "array",
+ "maxItems": 20,
+ "items": {
+ "$ref": "recipe-suggestion.v1.json"
+ }
+ }
+ }
+ }
+ ]
+ }
}
}
}
}
}
}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Food API",
"title": "OpenVibe.Food API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -271,6 +271,132 @@
@@ -271,6 +271,132 @@
}
}
}
}
}
}
+ },
+ "/api/v1/recipes": {
+ "get": {
+ "operationId": "get_api_v1_recipes",
+ "summary": "Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to ",
+ "description": "**food.recipes.suggest** (public): Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to Food's food list; what is still to buy priced from Food's own typical prices), 10 a person a day; GET /api/v1/recipes lists the last 20. A person acting for themself needs no capability; an app or agent token needs this one.",
+ "tags": [
+ "food.recipes"
+ ],
+ "x-openvibe-capabilities": [
+ "food.recipes.suggest"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.recipes.suggest"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/food.recipe-suggest-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "ask",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 500
+ }
+ },
+ {
+ "name": "people",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.recipe-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/recipes/suggest": {
+ "post": {
+ "operationId": "post_api_v1_recipes_suggest",
+ "summary": "Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to ",
+ "description": "**food.recipes.suggest** (public): Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to Food's food list; what is still to buy priced from Food's own typical prices), 10 a person a day; GET /api/v1/recipes lists the last 20. A person acting for themself needs no capability; an app or agent token needs this one.",
+ "tags": [
+ "food.recipes"
+ ],
+ "x-openvibe-capabilities": [
+ "food.recipes.suggest"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "food.recipes.suggest"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.recipe-suggest-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/food.recipe-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"components": {
"components": {
@@ -642,6 +768,222 @@
@@ -642,6 +768,222 @@
}
}
}
}
}
}
+ },
+ "food.recipe-result.v1": {
+ "title": "FoodRecipeResult",
+ "description": "food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).",
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/food.recipe-suggestion.v1"
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "recipes"
+ ],
+ "properties": {
+ "recipes": {
+ "type": "array",
+ "maxItems": 20,
+ "items": {
+ "$ref": "#/components/schemas/food.recipe-suggestion.v1"
+ }
+ }
+ }
+ }
+ ]
+ },
+ "food.recipe-suggest-request.v1": {
+ "title": "FoodRecipeSuggestRequest",
+ "description": "food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "ask": {
+ "type": "string",
+ "maxLength": 500
+ },
+ "people": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 12
+ }
+ }
+ },
+ "food.recipe-suggestion.v1": {
+ "title": "FoodRecipeSuggestion",
+ "description": "food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "created_at",
+ "ask",
+ "people",
+ "ai_run_id",
+ "recipes"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^rcp_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "ask": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 500
+ },
+ "people": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 12
+ },
+ "ai_run_id": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "recipes": {
+ "type": "array",
+ "maxItems": 3,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "title",
+ "ingredients",
+ "steps",
+ "to_buy",
+ "to_buy_cost",
+ "cost_note"
+ ],
+ "properties": {
+ "title": {
+ "type": "string",
+ "maxLength": 160
+ },
+ "serves": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 12
+ },
+ "minutes": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "maximum": 600
+ },
+ "ingredients": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 25,
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "name",
+ "amount",
+ "from_pantry",
+ "citations"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "maxLength": 120
+ },
+ "amount": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 80
+ },
+ "from_pantry": {
+ "type": "boolean"
+ },
+ "citations": {
+ "type": "array",
+ "items": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ }
+ },
+ "steps": {
+ "type": "array",
+ "minItems": 1,
+ "maxItems": 20,
+ "items": {
+ "type": "string",
+ "maxLength": 600
+ }
+ },
+ "notes": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 600
+ },
+ "to_buy": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "slug",
+ "name",
+ "price"
+ ],
+ "properties": {
+ "slug": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "price": {
+ "type": [
+ "number",
+ "null"
+ ],
+ "minimum": 0
+ }
+ }
+ }
+ },
+ "to_buy_cost": {
+ "type": "number",
+ "minimum": 0
+ },
+ "to_buy_cost_partial": {
+ "type": "boolean"
+ },
+ "cost_note": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
}
}
},
},
"securitySchemes": {
"securitySchemes": {
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -75,8 +75,8 @@
@@ -75,8 +75,8 @@
"service": "food",
"service": "food",
"name": "OpenVibe.Food",
"name": "OpenVibe.Food",
"origin": "https://openvibe.food",
"origin": "https://openvibe.food",
- "operations": 5,
- "capabilities": 4
+ "operations": 7,
+ "capabilities": 5
},
},
{
{
"service": "games",
"service": "games",
@@ -210,6 +210,13 @@
@@ -210,6 +210,13 @@
"origin": "https://openvibe.wiki",
"origin": "https://openvibe.wiki",
"operations": 25,
"operations": 25,
"capabilities": 8
"capabilities": 8
+ },
+ {
+ "service": "work",
+ "name": "OpenVibe.Work",
+ "origin": "https://openvibe.work",
+ "operations": 5,
+ "capabilities": 1
}
}
]
]
}
}
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.146.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.146.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.146.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
new file mode 100644
new file mode 100644
@@ -0,0 +1,918 @@
@@ -0,0 +1,918 @@
+{
+ "openapi": "3.1.0",
+ "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
+ "info": {
+ "title": "OpenVibe.Work API",
+ "version": "0.147.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.147.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "license": {
+ "name": "MIT",
+ "identifier": "MIT"
+ }
+ },
+ "servers": [
+ {
+ "url": "https://openvibe.work"
+ }
+ ],
+ "paths": {
+ "/api/v1/me/applications": {
+ "get": {
+ "operationId": "get_api_v1_me_applications",
+ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",
+ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",
+ "tags": [
+ "work.application"
+ ],
+ "x-openvibe-capabilities": [
+ "work.application.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.application.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/work.application-write-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "listing_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "title",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ }
+ },
+ {
+ "name": "company",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "url",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ }
+ },
+ {
+ "name": "status",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ }
+ },
+ {
+ "name": "notes",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 5000
+ }
+ },
+ {
+ "name": "applied_at",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ },
+ {
+ "name": "next_step",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "next_step_at",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "post": {
+ "operationId": "post_api_v1_me_applications",
+ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",
+ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",
+ "tags": [
+ "work.application"
+ ],
+ "x-openvibe-capabilities": [
+ "work.application.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.application.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-write-request.v1"
+ }
+ }
+ }
+ },
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/api/v1/me/applications/{id}": {
+ "delete": {
+ "operationId": "delete_api_v1_me_applications_id",
+ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",
+ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",
+ "tags": [
+ "work.application"
+ ],
+ "x-openvibe-capabilities": [
+ "work.application.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.application.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/work.application-write-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "listing_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "title",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ }
+ },
+ {
+ "name": "company",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "url",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ }
+ },
+ {
+ "name": "status",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ }
+ },
+ {
+ "name": "notes",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 5000
+ }
+ },
+ {
+ "name": "applied_at",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ },
+ {
+ "name": "next_step",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "next_step_at",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "get": {
+ "operationId": "get_api_v1_me_applications_id",
+ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",
+ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",
+ "tags": [
+ "work.application"
+ ],
+ "x-openvibe-capabilities": [
+ "work.application.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.application.manage"
+ ]
+ }
+ ],
+ "x-openvibe-input": {
+ "$ref": "#/components/schemas/work.application-write-request.v1"
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "listing_id",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "title",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ }
+ },
+ {
+ "name": "company",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "url",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ }
+ },
+ {
+ "name": "status",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ }
+ },
+ {
+ "name": "notes",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 5000
+ }
+ },
+ {
+ "name": "applied_at",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ },
+ {
+ "name": "next_step",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": "string",
+ "maxLength": 200
+ }
+ },
+ {
+ "name": "next_step_at",
+ "in": "query",
+ "required": false,
+ "schema": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "patch": {
+ "operationId": "patch_api_v1_me_applications_id",
+ "summary": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and nex",
+ "description": "**work.application.manage** (public): Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",
+ "tags": [
+ "work.application"
+ ],
+ "x-openvibe-capabilities": [
+ "work.application.manage"
+ ],
+ "x-openvibe-visibility": [
+ "public"
+ ],
+ "x-openvibe-quota-class": "standard",
+ "security": [
+ {
+ "openvibeToken": [
+ "work.application.manage"
+ ]
+ }
+ ],
+ "requestBody": {
+ "required": false,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-write-request.v1"
+ }
+ }
+ }
+ },
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "2XX": {
+ "description": "Success",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/work.application-result.v1"
+ }
+ }
+ }
+ },
+ "default": {
+ "description": "An error (RFC 9457 problem details)",
+ "content": {
+ "application/problem+json": {
+ "schema": {
+ "$ref": "#/components/schemas/errors.problem.v1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "errors.problem.v1": {
+ "title": "Problem",
+ "description": "RFC 9457 problem details (application/problem+json) with a stable OpenVibe error code and trace identifiers. The legacy 'error' string is allowed during migration so existing clients keep working.",
+ "type": "object",
+ "required": [
+ "type",
+ "title",
+ "status",
+ "code"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "type": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1
+ },
+ "status": {
+ "type": "integer",
+ "minimum": 400,
+ "maximum": 599
+ },
+ "detail": {
+ "type": "string"
+ },
+ "instance": {
+ "type": "string",
+ "format": "uri-reference"
+ },
+ "code": {
+ "type": "string",
+ "pattern": "^[a-z][a-z0-9_]*(\\.[a-z0-9_]+)*$"
+ },
+ "request_id": {
+ "type": "string"
+ },
+ "trace_id": {
+ "type": "string",
+ "pattern": "^[0-9a-f]{32}$"
+ },
+ "errors": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "message"
+ ],
+ "properties": {
+ "path": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "error": {
+ "type": "string",
+ "description": "Deprecated compatibility field: same text as title/detail for clients that read {error}."
+ }
+ }
+ },
+ "work.application-result.v1": {
+ "title": "WorkApplicationResult",
+ "description": "work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.",
+ "anyOf": [
+ {
+ "$ref": "#/components/schemas/work.application.v1"
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "applications",
+ "next",
+ "count"
+ ],
+ "properties": {
+ "applications": {
+ "type": "array",
+ "maxItems": 100,
+ "items": {
+ "$ref": "#/components/schemas/work.application.v1"
+ }
+ },
+ "next": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "count": {
+ "type": "integer",
+ "minimum": 0
+ }
+ }
+ }
+ ]
+ },
+ "work.application-write-request.v1": {
+ "title": "WorkApplicationWriteRequest",
+ "description": "work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.",
+ "type": "object",
+ "additionalProperties": false,
+ "minProperties": 1,
+ "properties": {
+ "listing_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ },
+ "status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "notes": {
+ "type": "string",
+ "maxLength": 5000
+ },
+ "applied_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "next_step": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "next_step_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ }
+ }
+ },
+ "work.application.v1": {
+ "title": "WorkApplication",
+ "description": "work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "subject_id",
+ "listing_id",
+ "title",
+ "company",
+ "url",
+ "status",
+ "notes",
+ "applied_at",
+ "next_step",
+ "next_step_at",
+ "actor_task_id",
+ "assists",
+ "created_at",
+ "updated_at"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^wap_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "subject_id": {
+ "type": "string",
+ "pattern": "^user:usr_[0-9A-HJKMNP-TV-Z]{26}$"
+ },
+ "listing_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200,
+ "description": "The Work listing it was tracked from, or null when added by hand."
+ },
+ "title": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 500
+ },
+ "company": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 200
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2000
+ },
+ "status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "notes": {
+ "type": "string",
+ "maxLength": 5000
+ },
+ "applied_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "next_step": {
+ "type": "string",
+ "maxLength": 200
+ },
+ "next_step_at": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "format": "date-time"
+ },
+ "actor_task_id": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "maxLength": 200,
+ "description": "The latest OpenVibe.Actor task asked for this application (POST …/assist)."
+ },
+ "assists": {
+ "type": "integer",
+ "minimum": 0
+ },
+ "created_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updated_at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "events": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "at",
+ "from_status",
+ "to_status"
+ ],
+ "properties": {
+ "at": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "from_status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ },
+ "to_status": {
+ "enum": [
+ "saved",
+ "applied",
+ "interviewing",
+ "offer",
+ "rejected",
+ "withdrawn"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "securitySchemes": {
+ "openvibeToken": {
+ "type": "http",
+ "scheme": "bearer",
+ "bearerFormat": "JWT",
+ "description": "An OpenVibe Network token for this service: a person's token, an app token (client credentials, audience of the service) or a first-party service token. Its `cap` claim must include the capability the operation names."
+ }
+ }
+ }
+}
@@ -171633,3 +171633,1441 @@ export interface QuestIndexDocumentDeletedPayload {
@@ -171633,3 +171633,1441 @@ export interface QuestIndexDocumentDeletedPayload {
*/
*/
revision: number;
revision: number;
}
}
+
+/** [email protected] (owner: work) */
+/**
+ * work.application@1: a person's tracked job application on OpenVibe.Work (server/applications.js toWire), as GET/POST/PATCH /api/v1/me/applications[/:id] answer it. GET of one application adds its append-only status changes as `events`.
+ */
+export interface WorkApplication {
+ id: string;
+ subject_id: string;
+ /**
+ * The Work listing it was tracked from, or null when added by hand.
+ */
+ listing_id: string | null;
+ title: string;
+ company: string;
+ url: string;
+ status: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";
+ notes: string;
+ applied_at: string | null;
+ next_step: string;
+ next_step_at: string | null;
+ /**
+ * The latest OpenVibe.Actor task asked for this application (POST …/assist).
+ */
+ actor_task_id: string | null;
+ assists: number;
+ created_at: string;
+ updated_at: string;
+ events?: {
+ at: string;
+ from_status: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";
+ to_status: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";
+ }[];
+}
+
+/** [email protected] (owner: work) */
+/**
+ * work.application-write-request@1: bodies of work.application.manage. POST /api/v1/me/applications takes a listing_id (title, company and URL are copied from the listing; a second POST for the same listing answers the existing application with 200) or title + company + url by hand. PATCH /api/v1/me/applications/:id takes status, notes, applied_at, next_step and next_step_at; a status change appends an event, and status applied with no applied_at stamps it now. Unknown fields are 400.
+ */
+export interface WorkApplicationWriteRequest {
+ listing_id?: string;
+ title?: string;
+ company?: string;
+ url?: string;
+ status?: "saved" | "applied" | "interviewing" | "offer" | "rejected" | "withdrawn";
+ notes?: string;
+ applied_at?: string | null;
+ next_step?: string;
+ next_step_at?: string | null;
+}
+
+/** [email protected] (owner: work) */
+/**
+ * work.application-result@1: what work.application.manage answers: one application, or a page of them (GET /api/v1/me/applications, newest first, ?status= and ?cursor=; `next` is the next page's cursor or null). DELETE answers 204 with no body.
+ */
+export type WorkApplicationResult =
+ | WorkApplication
+ | {
+ /**
+ * @maxItems 100
+ */
+ applications: WorkApplication[];
+ next: string | null;
+ count: number;
+ };
+
+/** [email protected] (owner: food) */
+/**
+ * food.recipe-suggest-request@1: POST /api/v1/recipes/suggest. Asks OpenVibe.AI's food.suggest_recipes workflow for up to three recipes from the caller's saved pantry (an empty pantry is 422 food.pantry_empty). 10 a person a day (429 food.recipes_daily_limit); with Food's AI connection off, 503 food.ai_off.
+ */
+export interface FoodRecipeSuggestRequest {
+ ask?: string;
+ people?: number;
+}
+
+/** [email protected] (owner: food) */
+/**
+ * food.recipe-suggestion@1: one stored answer (the last 20 a person are kept). Ingredients are from the pantry or cite the food-list rows Food sent the workflow; to_buy and to_buy_cost are Food's own typical prices, never the model's. A recipe is AI-suggested and labelled so on the page.
+ */
+export interface FoodRecipeSuggestion {
+ id: string;
+ created_at: string;
+ ask: string | null;
+ people: number | null;
+ ai_run_id: string | null;
+ /**
+ * @maxItems 3
+ */
+ recipes:
+ | []
+ | [
+ {
+ title: string;
+ serves?: number | null;
+ minutes?: number | null;
+ /**
+ * @minItems 1
+ * @maxItems 25
+ */
+ ingredients: [
+ {
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ },
+ ...{
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ }[]
+ ];
+ /**
+ * @minItems 1
+ * @maxItems 20
+ */
+ steps:
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string, string]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ notes?: string | null;
+ to_buy: {
+ slug: string;
+ name: string;
+ price: number | null;
+ }[];
+ to_buy_cost: number;
+ to_buy_cost_partial?: boolean;
+ cost_note: string;
+ }
+ ]
+ | [
+ {
+ title: string;
+ serves?: number | null;
+ minutes?: number | null;
+ /**
+ * @minItems 1
+ * @maxItems 25
+ */
+ ingredients: [
+ {
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ },
+ ...{
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ }[]
+ ];
+ /**
+ * @minItems 1
+ * @maxItems 20
+ */
+ steps:
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string, string]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ notes?: string | null;
+ to_buy: {
+ slug: string;
+ name: string;
+ price: number | null;
+ }[];
+ to_buy_cost: number;
+ to_buy_cost_partial?: boolean;
+ cost_note: string;
+ },
+ {
+ title: string;
+ serves?: number | null;
+ minutes?: number | null;
+ /**
+ * @minItems 1
+ * @maxItems 25
+ */
+ ingredients: [
+ {
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ },
+ ...{
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ }[]
+ ];
+ /**
+ * @minItems 1
+ * @maxItems 20
+ */
+ steps:
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string, string]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ notes?: string | null;
+ to_buy: {
+ slug: string;
+ name: string;
+ price: number | null;
+ }[];
+ to_buy_cost: number;
+ to_buy_cost_partial?: boolean;
+ cost_note: string;
+ }
+ ]
+ | [
+ {
+ title: string;
+ serves?: number | null;
+ minutes?: number | null;
+ /**
+ * @minItems 1
+ * @maxItems 25
+ */
+ ingredients: [
+ {
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ },
+ ...{
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ }[]
+ ];
+ /**
+ * @minItems 1
+ * @maxItems 20
+ */
+ steps:
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string, string]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ notes?: string | null;
+ to_buy: {
+ slug: string;
+ name: string;
+ price: number | null;
+ }[];
+ to_buy_cost: number;
+ to_buy_cost_partial?: boolean;
+ cost_note: string;
+ },
+ {
+ title: string;
+ serves?: number | null;
+ minutes?: number | null;
+ /**
+ * @minItems 1
+ * @maxItems 25
+ */
+ ingredients: [
+ {
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ },
+ ...{
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ }[]
+ ];
+ /**
+ * @minItems 1
+ * @maxItems 20
+ */
+ steps:
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string, string]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ notes?: string | null;
+ to_buy: {
+ slug: string;
+ name: string;
+ price: number | null;
+ }[];
+ to_buy_cost: number;
+ to_buy_cost_partial?: boolean;
+ cost_note: string;
+ },
+ {
+ title: string;
+ serves?: number | null;
+ minutes?: number | null;
+ /**
+ * @minItems 1
+ * @maxItems 25
+ */
+ ingredients: [
+ {
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ },
+ ...{
+ name: string;
+ amount: string | null;
+ from_pantry: boolean;
+ citations: number[];
+ }[]
+ ];
+ /**
+ * @minItems 1
+ * @maxItems 20
+ */
+ steps:
+ | [string]
+ | [string, string]
+ | [string, string, string]
+ | [string, string, string, string]
+ | [string, string, string, string, string]
+ | [string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string]
+ | [string, string, string, string, string, string, string, string, string, string, string, string, string]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ]
+ | [
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string,
+ string
+ ];
+ notes?: string | null;
+ to_buy: {
+ slug: string;
+ name: string;
+ price: number | null;
+ }[];
+ to_buy_cost: number;
+ to_buy_cost_partial?: boolean;
+ cost_note: string;
+ }
+ ];
+}
+
+/** [email protected] (owner: food) */
+/**
+ * food.recipe-result@1: what food.recipes.suggest answers: the new suggestion (POST /api/v1/recipes/suggest, 201) or the caller's recent ones (GET /api/v1/recipes).
+ */
+export type FoodRecipeResult =
+ | FoodRecipeSuggestion
+ | {
+ /**
+ * @maxItems 20
+ */
+ recipes:
+ | []
+ | [FoodRecipeSuggestion]
+ | [FoodRecipeSuggestion, FoodRecipeSuggestion]
+ | [FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion]
+ | [FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion]
+ | [FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion, FoodRecipeSuggestion]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ]
+ | [
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion,
+ FoodRecipeSuggestion
+ ];
+ };
new file mode 100644
new file mode 100644
@@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
+{
+ "id": "food.recipes.suggest",
+ "version": "1.0",
+ "owner": "food",
+ "status": "active",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "Recipe ideas from the caller's saved pantry: POST /api/v1/recipes/suggest runs OpenVibe.AI's food.suggest_recipes workflow for the caller (up to three recipes; ingredients from the pantry or cited to Food's food list; what is still to buy priced from Food's own typical prices), 10 a person a day; GET /api/v1/recipes lists the last 20. A person acting for themself needs no capability; an app or agent token needs this one.",
+ "inputSchema": "food.recipe-suggest-request@1",
+ "outputSchema": "food.recipe-result@1",
+ "permissions": [
+ "food:recipes:suggest"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "POST /api/v1/recipes/suggest",
+ "GET /api/v1/recipes"
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,26 @@
@@ -0,0 +1,26 @@
+{
+ "id": "work.application.manage",
+ "version": "1.0",
+ "owner": "work",
+ "status": "active",
+ "visibility": "public",
+ "sensitive": true,
+ "description": "Manage the caller's tracked job applications on OpenVibe.Work: list (GET /api/v1/me/applications), add from a listing or by hand (POST), read with its status history, edit status, notes, dates and next step (PATCH), and delete (DELETE). A person acting for themself needs no capability; an app or agent token needs this one. Asking the person's OpenVibe.Actor agent (POST …/assist) is the person's own act and takes no capability.",
+ "inputSchema": "work.application-write-request@1",
+ "outputSchema": "work.application-result@1",
+ "permissions": [
+ "work:application:manage"
+ ],
+ "resourceConstraints": [
+ "owner"
+ ],
+ "quotaClass": "standard",
+ "events": [],
+ "implementedBy": [
+ "GET /api/v1/me/applications",
+ "POST /api/v1/me/applications",
+ "GET /api/v1/me/applications/:id",
+ "PATCH /api/v1/me/applications/:id",
+ "DELETE /api/v1/me/applications/:id"
+ ]
+}
@@ -19,7 +19,8 @@
@@ -19,7 +19,8 @@
"food.plan.read",
"food.plan.read",
"food.plan.write",
"food.plan.write",
"food.pantry.read",
"food.pantry.read",
- "food.pantry.write"
+ "food.pantry.write",
+ "food.recipes.suggest"
],
],
"eventsProduced": [],
"eventsProduced": [],
"eventsConsumed": [
"eventsConsumed": [
@@ -15,7 +15,9 @@
@@ -15,7 +15,9 @@
"state": "live",
"state": "live",
"publicSite": "service"
"publicSite": "service"
},
},
- "capabilities": [],
+ "capabilities": [
+ "work.application.manage"
+ ],
"eventsProduced": [
"eventsProduced": [
"work.index_document.upserted",
"work.index_document.upserted",
"work.index_document.deleted"
"work.index_document.deleted"
@@ -71,7 +73,7 @@
@@ -71,7 +73,7 @@
"none": "no claims: one process serves the site"
"none": "no claims: one process serves the site"
}
}
},
},
- "notes": "Port 4960, unit openvibe-work.service. Job listings gathered from open job-board APIs (Arbeitnow, Remotive, RemoteOK) with their provenance: a short plain-text excerpt only, the source named and the original listing linked on every list and page, never the full description. Search with remote/type/location filters, JobPosting JSON-LD, and saved searches with a count of what is new since you last looked. The ingest runs every 6 hours per source when WORK_INGEST=on, upserts by (source, source_id) and expires listings not seen for 30 days. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client work, JWKS). Owns work_listings, work_source_fetches and work_saved_searches. No capabilities yet: saved searches are a person's, listings are public.",
+ "notes": "Port 4960, unit openvibe-work.service. Job listings gathered from open job-board APIs (Arbeitnow, Remotive, RemoteOK) with their provenance: a short plain-text excerpt only, the source named and the original listing linked on every list and page, never the full description. Search with remote/type/location filters, JobPosting JSON-LD, and saved searches with a count of what is new since you last looked. The ingest runs every 6 hours per source when WORK_INGEST=on, upserts by (source, source_id) and expires listings not seen for 30 days. Consumes OpenVibe.Network (SSO with PKCE S256 as OAuth client work, JWKS). Owns work_listings, work_source_fetches, work_saved_searches, work_applications and work_application_events. The agent pillar: a person tracks applications (status, notes, next steps, history; work.application.manage for apps and agents) and can ask their OpenVibe.Actor agent to help prepare one (actor.task.create with X-OV-Subject, idempotency key per assist, 10 a day).",
"health": "/api/health",
"health": "/api/health",
"ready": "/api/ready",
"ready": "/api/ready",
"internalOrigin": "http://127.0.0.1:4960",
"internalOrigin": "http://127.0.0.1:4960",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.146.0",
+ "version": "0.147.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",