> For the complete documentation index, see [llms.txt](https://mobile-calendar.gitbook.io/v1/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mobile-calendar.gitbook.io/v1/rest-api/rates.md).

# Rates

Get all rates.

## Get all rates

> Returns all active rate plans of the account with their cancellation policies. Rate plans are referenced by \`rateId\` in GET/PUT /pricing and in reservations. Coverage: \`rooms\` (rooms without a room type) and \`types\` (room types) — \`null\` = all, \`\[]\` = none, list = only those. Required scope: \`rates.read\`.

```json
{"openapi":"3.0.0","info":{"title":"Public REST API v1 – mobile-calendar.com","version":"1.0.0"},"tags":[{"name":"rates","description":"Get all rates."}],"servers":[{"url":"https://api.mobile-calendar.com/v1/public","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"Pass the token in header: Authorization: Bearer <JWT>","bearerFormat":"JWT","scheme":"bearer"}},"schemas":{"RatesResponse":{"description":"Response containing all available rates","required":["data","meta"],"properties":{"data":{"description":"List of all available rates","type":"array","items":{"$ref":"#/components/schemas/RateEntry"}},"meta":{"$ref":"#/components/schemas/MetaWithRuid"}},"type":"object"},"RateEntry":{"description":"Rate plan. Prices per day are managed via GET/PUT /pricing (`rateId`); a derived rate (`parentRateId` set) has its prices calculated from the parent rate using `rateRelation` and cannot have prices set directly.","required":["rateId","name","cancellationId","meals","parentRateId","rateRelation","rooms","types","editDate","cancellationPolicy"],"properties":{"rateId":{"description":"Unique rate identifier","type":"integer"},"name":{"description":"Rate name","type":"string","maxLength":50},"cancellationId":{"description":"Cancellation policy ID (`0` = the built-in default policy)","type":"integer"},"meals":{"description":"Meal plan: 0 = OV (no meals), 1 = BB (breakfast), 2 = HB (breakfast + lunch), 3 = FB (breakfast + lunch + dinner), 4 = AI (all inclusive), 5 = lunch, 6 = breakfast + dinner, 7 = lunch + dinner, 8 = dinner","type":"integer","enum":[0,1,2,3,4,5,6,7,8]},"parentRateId":{"description":"Parent rate ID for a derived rate (prices calculated from the parent via `rateRelation`); `null` for a standalone rate","type":"integer","nullable":true},"rateRelation":{"oneOf":[{"$ref":"#/components/schemas/RateRelation"}],"nullable":true},"rooms":{"description":"Rooms WITHOUT a room type (priced individually) this rate applies to. `null` = all such rooms (also ones added later), `[]` = none, `[ids]` = only those. Rooms that belong to a room type are covered through `types`.","type":"array","items":{"type":"integer"},"nullable":true},"types":{"description":"Room types this rate applies to (covers every room of the type). `null` = all types (also ones added later), `[]` = none, `[ids]` = only those.","type":"array","items":{"type":"integer"},"nullable":true},"editDate":{"description":"Last modification time (UTC, ISO 8601) — same format as `addDate`/`editDate` of other resources.","type":"string","format":"date-time"},"cancellationPolicy":{"$ref":"#/components/schemas/CancellationPolicy"}},"type":"object"},"RateRelation":{"description":"How a derived rate's prices are calculated from its parent — exactly one of `percentage` or `additional` is set, the other is `null`. A parent price of 0 always gives 0.","properties":{"percentage":{"description":"Multiplier relative to the parent rate, 100-based: 100 = same price, 115 = +15%, 90 = -10%. Range 0.01–1000, at most 2 decimals. `null` when `additional` is used.","type":"number","format":"float","nullable":true},"additional":{"description":"Fixed amount added to the parent price (negative = discount), in the account currency, at most 2 decimals. `null` when `percentage` is used.","type":"number","format":"float","nullable":true}},"type":"object"},"CancellationPolicy":{"description":"Cancellation policy details","required":["cancellationId","type"],"properties":{"cancellationId":{"type":"integer"},"type":{"type":"string","enum":["REFUNDABLE","NONREFUNDABLE"]},"cancellationPenaltyPercentageAfterDeadline":{"description":"Penalty percentage after deadline","type":"integer","nullable":true},"deadlineDays":{"description":"Deadline in days before arrival","type":"integer","nullable":true},"deadlineHours":{"description":"Deadline in hours before arrival","type":"integer","nullable":true},"cancellationPenaltyNightsAfterDeadline":{"description":"Penalty in nights after deadline","type":"integer","nullable":true},"noShow":{"description":"No-show policy","type":"string"},"prepaymentPercentage":{"description":"Required prepayment percentage","type":"integer","nullable":true},"prepaymentFirstNight":{"description":"Whether prepayment covers first night only","type":"boolean"}},"type":"object"},"MetaWithRuid":{"description":"Metadata with request tracking.","required":["ruid"],"properties":{"ruid":{"description":"Request Unique ID","type":"string","format":"uuid"}},"type":"object"},"ApiProblem401":{"description":"Missing authentication or invalid credentials.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"401 Unauthorized","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblemBase":{"description":"RFC 7807 Problem Details (base schema).","required":["type","title","status","detail","instance"],"properties":{"type":{"description":"RFC 7807 base — DO NOT use directly in endpoints.\nIn endpoints reference ApiProblem400/401/403/409/422/429/500.","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"},"ApiProblem403":{"description":"Authenticated user but no access.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"403 Forbidden","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]}}},"paths":{"/rates":{"get":{"tags":["rates"],"summary":"Get all rates","description":"Returns all active rate plans of the account with their cancellation policies. Rate plans are referenced by `rateId` in GET/PUT /pricing and in reservations. Coverage: `rooms` (rooms without a room type) and `types` (room types) — `null` = all, `[]` = none, list = only those. Required scope: `rates.read`.","operationId":"getRates","responses":{"200":{"description":"List of all available rates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RatesResponse"}}}},"401":{"description":"Unauthorized - invalid or missing authentication token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem401"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem403"}}}}}}}}}
```

## Create rate

> Creates a rate plan. Only \`name\` is required (1–50 bytes). Defaults: default cancellation policy (\`cancellationId: 0\`), no meals, standalone (no parent), ALL rooms and ALL room types.\
> \
> Room coverage — omit \`rooms\` and \`types\` unless the user explicitly limits the plan to some rooms or room types; omitted/\`null\` = all, including rooms and types added later. Never list every ID to mean "all" (a list does not cover rooms/types added later). The two fields are independent: \`rooms\` covers only rooms WITHOUT a room type (priced individually), \`types\` covers room types (a room that belongs to a type is covered through its type — do not put its ID in \`rooms\`). To limit the plan to specific individual rooms only, send \`rooms: \[ids]\` AND \`types: \[]\`; to specific room types only, \`types: \[ids]\` AND \`rooms: \[]\`; otherwise the other field stays "all". Both \`\[]\` → 422.\
> \
> A derived rate (\`parentRateId\` + \`rateRelation\`) gets its daily prices calculated from the parent automatically; a standalone rate has no prices until they are set with PUT /pricing. Required scope: \`rates.create\`.

```json
{"openapi":"3.0.0","info":{"title":"Public REST API v1 – mobile-calendar.com","version":"1.0.0"},"tags":[{"name":"rates","description":"Get all rates."}],"servers":[{"url":"https://api.mobile-calendar.com/v1/public","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"Pass the token in header: Authorization: Bearer <JWT>","bearerFormat":"JWT","scheme":"bearer"}},"schemas":{"RateCreateRequest":{"description":"New rate plan. Only `name` is required — defaults: default cancellation policy (`cancellationId: 0`), no meals, standalone (no parent), all rooms and all room types. For a derived rate send `parentRateId` together with `rateRelation` (exactly one of `percentage` / `additional`, see RateRelation). `rooms` (rooms without a room type) and `types` (room types) are independent and default to all — omit them unless the plan is limited; they must not both be empty lists. IDs must belong to the account (GET /rooms, GET /room-types, GET /rates).","required":["name"],"properties":{"name":{"description":"Rate name (1–50 bytes, plain text).","type":"string","maxLength":50},"cancellationId":{"description":"Cancellation policy ID — one of the policies visible in GET /rates (`cancellationPolicy.cancellationId`); `0` = default policy.","type":"integer","default":0,"minimum":0},"meals":{"description":"Meal plan (see RateEntry.meals).","type":"integer","default":0,"enum":[0,1,2,3,4,5,6,7,8]},"parentRateId":{"description":"Parent rate for a derived rate; requires `rateRelation`. `null`/omitted = standalone rate.","type":"integer","minimum":1,"nullable":true},"rateRelation":{"oneOf":[{"$ref":"#/components/schemas/RateRelation"}],"nullable":true},"rooms":{"description":"Rooms WITHOUT a room type this rate applies to. Omit (or `null`) = all such rooms, also ones added later — the default; do not list IDs unless the user limits the plan. `[]` = none (use with `types: [ids]` for a types-only plan). List of room IDs = only those (to limit the plan to those rooms only, send also `types: []`). Rooms belonging to a room type are covered through `types`.","type":"array","items":{"type":"integer"},"maxItems":500,"nullable":true},"types":{"description":"Room types this rate applies to (every room of the type). Omit (or `null`) = all types, also ones added later — the default; do not list IDs unless the user limits the plan. `[]` = none (use with `rooms: [ids]` for a rooms-only plan). List of room type IDs = only those (to limit the plan to those types only, send also `rooms: []`).","type":"array","items":{"type":"integer"},"maxItems":500,"nullable":true}},"type":"object"},"RateRelation":{"description":"How a derived rate's prices are calculated from its parent — exactly one of `percentage` or `additional` is set, the other is `null`. A parent price of 0 always gives 0.","properties":{"percentage":{"description":"Multiplier relative to the parent rate, 100-based: 100 = same price, 115 = +15%, 90 = -10%. Range 0.01–1000, at most 2 decimals. `null` when `additional` is used.","type":"number","format":"float","nullable":true},"additional":{"description":"Fixed amount added to the parent price (negative = discount), in the account currency, at most 2 decimals. `null` when `percentage` is used.","type":"number","format":"float","nullable":true}},"type":"object"},"RateResponse":{"description":"Single rate response with metadata","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/RateEntry"},"meta":{"$ref":"#/components/schemas/MetaWithRuid"}},"type":"object"},"RateEntry":{"description":"Rate plan. Prices per day are managed via GET/PUT /pricing (`rateId`); a derived rate (`parentRateId` set) has its prices calculated from the parent rate using `rateRelation` and cannot have prices set directly.","required":["rateId","name","cancellationId","meals","parentRateId","rateRelation","rooms","types","editDate","cancellationPolicy"],"properties":{"rateId":{"description":"Unique rate identifier","type":"integer"},"name":{"description":"Rate name","type":"string","maxLength":50},"cancellationId":{"description":"Cancellation policy ID (`0` = the built-in default policy)","type":"integer"},"meals":{"description":"Meal plan: 0 = OV (no meals), 1 = BB (breakfast), 2 = HB (breakfast + lunch), 3 = FB (breakfast + lunch + dinner), 4 = AI (all inclusive), 5 = lunch, 6 = breakfast + dinner, 7 = lunch + dinner, 8 = dinner","type":"integer","enum":[0,1,2,3,4,5,6,7,8]},"parentRateId":{"description":"Parent rate ID for a derived rate (prices calculated from the parent via `rateRelation`); `null` for a standalone rate","type":"integer","nullable":true},"rateRelation":{"oneOf":[{"$ref":"#/components/schemas/RateRelation"}],"nullable":true},"rooms":{"description":"Rooms WITHOUT a room type (priced individually) this rate applies to. `null` = all such rooms (also ones added later), `[]` = none, `[ids]` = only those. Rooms that belong to a room type are covered through `types`.","type":"array","items":{"type":"integer"},"nullable":true},"types":{"description":"Room types this rate applies to (covers every room of the type). `null` = all types (also ones added later), `[]` = none, `[ids]` = only those.","type":"array","items":{"type":"integer"},"nullable":true},"editDate":{"description":"Last modification time (UTC, ISO 8601) — same format as `addDate`/`editDate` of other resources.","type":"string","format":"date-time"},"cancellationPolicy":{"$ref":"#/components/schemas/CancellationPolicy"}},"type":"object"},"CancellationPolicy":{"description":"Cancellation policy details","required":["cancellationId","type"],"properties":{"cancellationId":{"type":"integer"},"type":{"type":"string","enum":["REFUNDABLE","NONREFUNDABLE"]},"cancellationPenaltyPercentageAfterDeadline":{"description":"Penalty percentage after deadline","type":"integer","nullable":true},"deadlineDays":{"description":"Deadline in days before arrival","type":"integer","nullable":true},"deadlineHours":{"description":"Deadline in hours before arrival","type":"integer","nullable":true},"cancellationPenaltyNightsAfterDeadline":{"description":"Penalty in nights after deadline","type":"integer","nullable":true},"noShow":{"description":"No-show policy","type":"string"},"prepaymentPercentage":{"description":"Required prepayment percentage","type":"integer","nullable":true},"prepaymentFirstNight":{"description":"Whether prepayment covers first night only","type":"boolean"}},"type":"object"},"MetaWithRuid":{"description":"Metadata with request tracking.","required":["ruid"],"properties":{"ruid":{"description":"Request Unique ID","type":"string","format":"uuid"}},"type":"object"},"ApiProblem400":{"description":"Request syntax/format error.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"400 Bad Request","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblemBase":{"description":"RFC 7807 Problem Details (base schema).","required":["type","title","status","detail","instance"],"properties":{"type":{"description":"RFC 7807 base — DO NOT use directly in endpoints.\nIn endpoints reference ApiProblem400/401/403/409/422/429/500.","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"},"ApiProblem401":{"description":"Missing authentication or invalid credentials.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"401 Unauthorized","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem403":{"description":"Authenticated user but no access.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"403 Forbidden","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem422":{"description":"Input data validation error.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"422 Unprocessable Entity (validation)","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"},"errors":{"description":"Map: field name → error message(s)","type":"object","additionalProperties":{"$ref":"#/components/schemas/ErrorValue"}}},"type":"object"}]},"ErrorValue":{"description":"Single error message or list of messages.","oneOf":[{"description":"Error value in error map can be:\n- single string, or\n- array of strings (multiple messages for one field).","type":"string"},{"type":"array","items":{"type":"string"}}]}}},"paths":{"/rates":{"post":{"tags":["rates"],"summary":"Create rate","description":"Creates a rate plan. Only `name` is required (1–50 bytes). Defaults: default cancellation policy (`cancellationId: 0`), no meals, standalone (no parent), ALL rooms and ALL room types.\n\nRoom coverage — omit `rooms` and `types` unless the user explicitly limits the plan to some rooms or room types; omitted/`null` = all, including rooms and types added later. Never list every ID to mean \"all\" (a list does not cover rooms/types added later). The two fields are independent: `rooms` covers only rooms WITHOUT a room type (priced individually), `types` covers room types (a room that belongs to a type is covered through its type — do not put its ID in `rooms`). To limit the plan to specific individual rooms only, send `rooms: [ids]` AND `types: []`; to specific room types only, `types: [ids]` AND `rooms: []`; otherwise the other field stays \"all\". Both `[]` → 422.\n\nA derived rate (`parentRateId` + `rateRelation`) gets its daily prices calculated from the parent automatically; a standalone rate has no prices until they are set with PUT /pricing. Required scope: `rates.create`.","operationId":"createRate","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateCreateRequest"}}}},"responses":{"201":{"description":"Created","headers":{"Location":{"description":"URL of the new resource","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem400"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem401"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem403"}}}},"422":{"description":"Validation error — e.g. unknown cancellation policy / room / room type ID, `parentRateId` without `rateRelation`, both `rooms` and `types` empty","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem422"}}}}}}}}}
```

## Get rate by ID

> Required scope: \`rates.read\`.

```json
{"openapi":"3.0.0","info":{"title":"Public REST API v1 – mobile-calendar.com","version":"1.0.0"},"tags":[{"name":"rates","description":"Get all rates."}],"servers":[{"url":"https://api.mobile-calendar.com/v1/public","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"Pass the token in header: Authorization: Bearer <JWT>","bearerFormat":"JWT","scheme":"bearer"}},"schemas":{"RateResponse":{"description":"Single rate response with metadata","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/RateEntry"},"meta":{"$ref":"#/components/schemas/MetaWithRuid"}},"type":"object"},"RateEntry":{"description":"Rate plan. Prices per day are managed via GET/PUT /pricing (`rateId`); a derived rate (`parentRateId` set) has its prices calculated from the parent rate using `rateRelation` and cannot have prices set directly.","required":["rateId","name","cancellationId","meals","parentRateId","rateRelation","rooms","types","editDate","cancellationPolicy"],"properties":{"rateId":{"description":"Unique rate identifier","type":"integer"},"name":{"description":"Rate name","type":"string","maxLength":50},"cancellationId":{"description":"Cancellation policy ID (`0` = the built-in default policy)","type":"integer"},"meals":{"description":"Meal plan: 0 = OV (no meals), 1 = BB (breakfast), 2 = HB (breakfast + lunch), 3 = FB (breakfast + lunch + dinner), 4 = AI (all inclusive), 5 = lunch, 6 = breakfast + dinner, 7 = lunch + dinner, 8 = dinner","type":"integer","enum":[0,1,2,3,4,5,6,7,8]},"parentRateId":{"description":"Parent rate ID for a derived rate (prices calculated from the parent via `rateRelation`); `null` for a standalone rate","type":"integer","nullable":true},"rateRelation":{"oneOf":[{"$ref":"#/components/schemas/RateRelation"}],"nullable":true},"rooms":{"description":"Rooms WITHOUT a room type (priced individually) this rate applies to. `null` = all such rooms (also ones added later), `[]` = none, `[ids]` = only those. Rooms that belong to a room type are covered through `types`.","type":"array","items":{"type":"integer"},"nullable":true},"types":{"description":"Room types this rate applies to (covers every room of the type). `null` = all types (also ones added later), `[]` = none, `[ids]` = only those.","type":"array","items":{"type":"integer"},"nullable":true},"editDate":{"description":"Last modification time (UTC, ISO 8601) — same format as `addDate`/`editDate` of other resources.","type":"string","format":"date-time"},"cancellationPolicy":{"$ref":"#/components/schemas/CancellationPolicy"}},"type":"object"},"RateRelation":{"description":"How a derived rate's prices are calculated from its parent — exactly one of `percentage` or `additional` is set, the other is `null`. A parent price of 0 always gives 0.","properties":{"percentage":{"description":"Multiplier relative to the parent rate, 100-based: 100 = same price, 115 = +15%, 90 = -10%. Range 0.01–1000, at most 2 decimals. `null` when `additional` is used.","type":"number","format":"float","nullable":true},"additional":{"description":"Fixed amount added to the parent price (negative = discount), in the account currency, at most 2 decimals. `null` when `percentage` is used.","type":"number","format":"float","nullable":true}},"type":"object"},"CancellationPolicy":{"description":"Cancellation policy details","required":["cancellationId","type"],"properties":{"cancellationId":{"type":"integer"},"type":{"type":"string","enum":["REFUNDABLE","NONREFUNDABLE"]},"cancellationPenaltyPercentageAfterDeadline":{"description":"Penalty percentage after deadline","type":"integer","nullable":true},"deadlineDays":{"description":"Deadline in days before arrival","type":"integer","nullable":true},"deadlineHours":{"description":"Deadline in hours before arrival","type":"integer","nullable":true},"cancellationPenaltyNightsAfterDeadline":{"description":"Penalty in nights after deadline","type":"integer","nullable":true},"noShow":{"description":"No-show policy","type":"string"},"prepaymentPercentage":{"description":"Required prepayment percentage","type":"integer","nullable":true},"prepaymentFirstNight":{"description":"Whether prepayment covers first night only","type":"boolean"}},"type":"object"},"MetaWithRuid":{"description":"Metadata with request tracking.","required":["ruid"],"properties":{"ruid":{"description":"Request Unique ID","type":"string","format":"uuid"}},"type":"object"},"ApiProblem401":{"description":"Missing authentication or invalid credentials.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"401 Unauthorized","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblemBase":{"description":"RFC 7807 Problem Details (base schema).","required":["type","title","status","detail","instance"],"properties":{"type":{"description":"RFC 7807 base — DO NOT use directly in endpoints.\nIn endpoints reference ApiProblem400/401/403/409/422/429/500.","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"},"ApiProblem403":{"description":"Authenticated user but no access.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"403 Forbidden","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem404":{"description":"Resource specified in the request was not found.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"404 Not Found","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]}}},"paths":{"/rates/{id}":{"get":{"tags":["rates"],"summary":"Get rate by ID","description":"Required scope: `rates.read`.","operationId":"getRate","parameters":[{"name":"id","in":"path","description":"Rate ID","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Rate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem401"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem403"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem404"}}}}}}}}}
```

## Delete rate

> Deletes a rate plan (soft delete — it disappears from the list, pricing and the booking engine) and clears its daily prices. A rate that still has derived rates cannot be deleted (409 with \`childRateIds\`) — delete or re-parent them first. Existing reservations keep their prices. Required scope: \`rates.delete\`.

```json
{"openapi":"3.0.0","info":{"title":"Public REST API v1 – mobile-calendar.com","version":"1.0.0"},"tags":[{"name":"rates","description":"Get all rates."}],"servers":[{"url":"https://api.mobile-calendar.com/v1/public","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"Pass the token in header: Authorization: Bearer <JWT>","bearerFormat":"JWT","scheme":"bearer"}},"schemas":{"ApiProblem401":{"description":"Missing authentication or invalid credentials.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"401 Unauthorized","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblemBase":{"description":"RFC 7807 Problem Details (base schema).","required":["type","title","status","detail","instance"],"properties":{"type":{"description":"RFC 7807 base — DO NOT use directly in endpoints.\nIn endpoints reference ApiProblem400/401/403/409/422/429/500.","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"},"ApiProblem403":{"description":"Authenticated user but no access.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"403 Forbidden","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem404":{"description":"Resource specified in the request was not found.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"404 Not Found","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem409":{"description":"Conflict with resource state or integrity constraints.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"409 Conflict","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]}}},"paths":{"/rates/{id}":{"delete":{"tags":["rates"],"summary":"Delete rate","description":"Deletes a rate plan (soft delete — it disappears from the list, pricing and the booking engine) and clears its daily prices. A rate that still has derived rates cannot be deleted (409 with `childRateIds`) — delete or re-parent them first. Existing reservations keep their prices. Required scope: `rates.delete`.","operationId":"deleteRate","parameters":[{"name":"id","in":"path","description":"Rate ID","required":true,"schema":{"type":"integer"}}],"responses":{"204":{"description":"Deleted successfully","headers":{"RUID":{"description":"Request unique identifier","schema":{"type":"string"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem401"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem403"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem404"}}}},"409":{"description":"Rate has derived rates (`childRateIds` lists them)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem409"}}}}}}}}}
```

## Update rate (partial)

> Updates only the fields present in the body; omitted fields keep their current values. Changing \`parentRateId\`/\`rateRelation\` immediately changes the calculated prices of the derived rate; \`parentRateId: null\` detaches the rate from its parent; a rate cannot become its own ancestor (cycle check).\
> \
> Room coverage — send \`rooms\`/\`types\` only to change which rooms the plan applies to. The sent value REPLACES the stored one (it is not appended): to add a room to a plan limited to a list, send the current list (getRate) plus the new ID; \`null\` = all. A plan whose \`rooms\` (or \`types\`) is \`null\` already covers every individually priced room (or every room type) — do not "assign" rooms to it before setting prices; sending a list would NARROW it. \`rooms\` covers only rooms without a room type, \`types\` covers room types (see createRate). Extend coverage only when PUT /pricing answers \`RATE\_NOT\_ASSIGNED\_TO\_ROOM\` / \`RATE\_NOT\_ASSIGNED\_TO\_ROOM\_TYPE\`. Required scope: \`rates.update\`.

```json
{"openapi":"3.0.0","info":{"title":"Public REST API v1 – mobile-calendar.com","version":"1.0.0"},"tags":[{"name":"rates","description":"Get all rates."}],"servers":[{"url":"https://api.mobile-calendar.com/v1/public","description":"Production"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","description":"Pass the token in header: Authorization: Bearer <JWT>","bearerFormat":"JWT","scheme":"bearer"}},"schemas":{"RateUpdateRequest":{"description":"Partial update — only the fields present in the body are changed; cross-field rules are checked on the merged result. `name`, `cancellationId` and `meals` must not be null when sent. `parentRateId: null` detaches the rate from its parent (and clears `rateRelation`); setting `parentRateId` requires a `rateRelation` (sent now or already present). `rooms`/`types` sent as `null` mean \"all\". Fields from the GET response that are not listed here (`rateId`, `cancellationPolicy`, `editDate`) are ignored.","properties":{"name":{"description":"Rate name (1–50 bytes, plain text). Not null.","type":"string","maxLength":50},"cancellationId":{"description":"Cancellation policy ID from GET /rates (`cancellationPolicy.cancellationId`); `0` = default policy. Not null.","type":"integer","minimum":0},"meals":{"description":"Meal plan (see RateEntry.meals: 0 = none, 1 = breakfast …). Not null.","type":"integer","enum":[0,1,2,3,4,5,6,7,8]},"parentRateId":{"description":"Parent rate — makes this a derived rate (requires `rateRelation`, sent now or already stored). `null` = detach from the parent (standalone rate).","type":"integer","minimum":1,"nullable":true},"rateRelation":{"oneOf":[{"$ref":"#/components/schemas/RateRelation"}],"nullable":true},"rooms":{"description":"Send only to change coverage of rooms WITHOUT a room type. REPLACES the stored value: `null` = all such rooms, `[]` = none, list = only those. If the stored value is `null`, the plan already covers every such room — sending a list narrows it.","type":"array","items":{"type":"integer"},"maxItems":500,"nullable":true},"types":{"description":"Send only to change room-type coverage. REPLACES the stored value: `null` = all types, `[]` = none, list = only those. If the stored value is `null`, the plan already covers every type — sending a list narrows it.","type":"array","items":{"type":"integer"},"maxItems":500,"nullable":true}},"type":"object"},"RateRelation":{"description":"How a derived rate's prices are calculated from its parent — exactly one of `percentage` or `additional` is set, the other is `null`. A parent price of 0 always gives 0.","properties":{"percentage":{"description":"Multiplier relative to the parent rate, 100-based: 100 = same price, 115 = +15%, 90 = -10%. Range 0.01–1000, at most 2 decimals. `null` when `additional` is used.","type":"number","format":"float","nullable":true},"additional":{"description":"Fixed amount added to the parent price (negative = discount), in the account currency, at most 2 decimals. `null` when `percentage` is used.","type":"number","format":"float","nullable":true}},"type":"object"},"RateResponse":{"description":"Single rate response with metadata","required":["data","meta"],"properties":{"data":{"$ref":"#/components/schemas/RateEntry"},"meta":{"$ref":"#/components/schemas/MetaWithRuid"}},"type":"object"},"RateEntry":{"description":"Rate plan. Prices per day are managed via GET/PUT /pricing (`rateId`); a derived rate (`parentRateId` set) has its prices calculated from the parent rate using `rateRelation` and cannot have prices set directly.","required":["rateId","name","cancellationId","meals","parentRateId","rateRelation","rooms","types","editDate","cancellationPolicy"],"properties":{"rateId":{"description":"Unique rate identifier","type":"integer"},"name":{"description":"Rate name","type":"string","maxLength":50},"cancellationId":{"description":"Cancellation policy ID (`0` = the built-in default policy)","type":"integer"},"meals":{"description":"Meal plan: 0 = OV (no meals), 1 = BB (breakfast), 2 = HB (breakfast + lunch), 3 = FB (breakfast + lunch + dinner), 4 = AI (all inclusive), 5 = lunch, 6 = breakfast + dinner, 7 = lunch + dinner, 8 = dinner","type":"integer","enum":[0,1,2,3,4,5,6,7,8]},"parentRateId":{"description":"Parent rate ID for a derived rate (prices calculated from the parent via `rateRelation`); `null` for a standalone rate","type":"integer","nullable":true},"rateRelation":{"oneOf":[{"$ref":"#/components/schemas/RateRelation"}],"nullable":true},"rooms":{"description":"Rooms WITHOUT a room type (priced individually) this rate applies to. `null` = all such rooms (also ones added later), `[]` = none, `[ids]` = only those. Rooms that belong to a room type are covered through `types`.","type":"array","items":{"type":"integer"},"nullable":true},"types":{"description":"Room types this rate applies to (covers every room of the type). `null` = all types (also ones added later), `[]` = none, `[ids]` = only those.","type":"array","items":{"type":"integer"},"nullable":true},"editDate":{"description":"Last modification time (UTC, ISO 8601) — same format as `addDate`/`editDate` of other resources.","type":"string","format":"date-time"},"cancellationPolicy":{"$ref":"#/components/schemas/CancellationPolicy"}},"type":"object"},"CancellationPolicy":{"description":"Cancellation policy details","required":["cancellationId","type"],"properties":{"cancellationId":{"type":"integer"},"type":{"type":"string","enum":["REFUNDABLE","NONREFUNDABLE"]},"cancellationPenaltyPercentageAfterDeadline":{"description":"Penalty percentage after deadline","type":"integer","nullable":true},"deadlineDays":{"description":"Deadline in days before arrival","type":"integer","nullable":true},"deadlineHours":{"description":"Deadline in hours before arrival","type":"integer","nullable":true},"cancellationPenaltyNightsAfterDeadline":{"description":"Penalty in nights after deadline","type":"integer","nullable":true},"noShow":{"description":"No-show policy","type":"string"},"prepaymentPercentage":{"description":"Required prepayment percentage","type":"integer","nullable":true},"prepaymentFirstNight":{"description":"Whether prepayment covers first night only","type":"boolean"}},"type":"object"},"MetaWithRuid":{"description":"Metadata with request tracking.","required":["ruid"],"properties":{"ruid":{"description":"Request Unique ID","type":"string","format":"uuid"}},"type":"object"},"ApiProblem400":{"description":"Request syntax/format error.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"400 Bad Request","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblemBase":{"description":"RFC 7807 Problem Details (base schema).","required":["type","title","status","detail","instance"],"properties":{"type":{"description":"RFC 7807 base — DO NOT use directly in endpoints.\nIn endpoints reference ApiProblem400/401/403/409/422/429/500.","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"},"ApiProblem401":{"description":"Missing authentication or invalid credentials.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"401 Unauthorized","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem403":{"description":"Authenticated user but no access.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"403 Forbidden","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem404":{"description":"Resource specified in the request was not found.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"404 Not Found","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}},"type":"object"}]},"ApiProblem422":{"description":"Input data validation error.","allOf":[{"$ref":"#/components/schemas/ApiProblemBase"},{"properties":{"type":{"description":"422 Unprocessable Entity (validation)","type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"},"errors":{"description":"Map: field name → error message(s)","type":"object","additionalProperties":{"$ref":"#/components/schemas/ErrorValue"}}},"type":"object"}]},"ErrorValue":{"description":"Single error message or list of messages.","oneOf":[{"description":"Error value in error map can be:\n- single string, or\n- array of strings (multiple messages for one field).","type":"string"},{"type":"array","items":{"type":"string"}}]}}},"paths":{"/rates/{id}":{"patch":{"tags":["rates"],"summary":"Update rate (partial)","description":"Updates only the fields present in the body; omitted fields keep their current values. Changing `parentRateId`/`rateRelation` immediately changes the calculated prices of the derived rate; `parentRateId: null` detaches the rate from its parent; a rate cannot become its own ancestor (cycle check).\n\nRoom coverage — send `rooms`/`types` only to change which rooms the plan applies to. The sent value REPLACES the stored one (it is not appended): to add a room to a plan limited to a list, send the current list (getRate) plus the new ID; `null` = all. A plan whose `rooms` (or `types`) is `null` already covers every individually priced room (or every room type) — do not \"assign\" rooms to it before setting prices; sending a list would NARROW it. `rooms` covers only rooms without a room type, `types` covers room types (see createRate). Extend coverage only when PUT /pricing answers `RATE_NOT_ASSIGNED_TO_ROOM` / `RATE_NOT_ASSIGNED_TO_ROOM_TYPE`. Required scope: `rates.update`.","operationId":"updateRate","parameters":[{"name":"id","in":"path","description":"Rate ID","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateUpdateRequest"}}}},"responses":{"200":{"description":"Updated rate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateResponse"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem400"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem401"}}}},"403":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem403"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem404"}}}},"422":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiProblem422"}}}}}}}}}
```
