> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.lettbestilt.no/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.lettbestilt.no/_mcp/server.

# Replace the weekly opening hours

PUT https://www.lettbestilt.no/api/v1/partner/restaurants/{slug}/hours
Content-Type: application/json

**Partner key + restaurant key.** Replaces the whole week with `days` (1–7 days, each weekday at most once; a weekday you leave out is closed). Times are `HH:MM` in Oslo time. `closesAt` before `opensAt` means open 00:00–`closesAt` and `opensAt`–24:00 on the same weekday, not a window into the next day.

Idempotent: the same week gives 200 without a change. The week has no `updatedAt`, so this takes no `expectedUpdatedAt` (it gives 422); two saves at the same time run one after the other and the last one wins. Overrides are not touched, and the opening hours step in the onboarding steps is not ticked off (the owner does that). Not allowed for an archived restaurant or the demo restaurant.

Reference: https://docs.lettbestilt.no/api-reference/partner/set-partner-restaurant-hours

## Authentication

- `Authorization` header (bearer token, required) — The partner key (`lbp_test_…` or `lbp_live_…`), sent as `Authorization: Bearer lbp_…` and created in LettBestilt Partner under **Partner key**. It belongs to the whole partner, not to a restaurant, and only works against `/api/v1/partner`. Server to server only: a request with an `Origin` header is rejected with `403 FORBIDDEN`, and the key must never be in a browser. A TEST key sees and creates only sandboxes; LIVE keys work once LettBestilt Partner is open. A suspended or archived partner, a revoked key and a restaurant key (`lbs_…`) here give `401 UNAUTHORIZED`; an expired key gives `401 KEY_EXPIRED`.
- `X-LettBestilt-Restaurant-Key` header (required) — The restaurant's own key (`lbs_test_…` or `lbs_live_…`) for calls about one restaurant in the partner API, sent together with the partner key. The key must have full access (scope `*`, otherwise `403 INSUFFICIENT_SCOPE`), belong to a restaurant the partner owns and be in the same environment as the partner key. A restaurant that does not exist, belongs to another partner or does not match the key always gives the same `404 RESTAURANT_NOT_FOUND`. Older keys without an environment in the prefix (`lbs_<random>`) are not accepted here; rotate them in LettBestilt Partner.

## Request

### Path parameters

- `slug` (string, required) — The restaurant's slug. It must match the restaurant key in `X-LettBestilt-Restaurant-Key`.

### Body (application/json)

This endpoint expects an object.

- `days` (list of PartnerOpeningDay, required)

## Response

### 200

The opening hours as they are now (the same shape as GET).

- `data` (PartnerHours, required)

## Errors

### 400 Bad Request Error

`INVALID_JSON`.

- `error` (ErrorBody, required)

### 401 Unauthorized Error

`UNAUTHORIZED`: a key is missing, invalid or revoked, the two keys are from different environments (TEST and LIVE), or the partner is suspended or archived. `KEY_EXPIRED`: a key is past its expiry date.

- `error` (ErrorBody, required)

### 403 Forbidden Error

`FORBIDDEN`: the request has an `Origin` header (server to server only). `PARTNER_AGREEMENT_REQUIRED`: a LIVE key, but the partner agreements are not signed. `PARTNER_NOT_APPROVED`: LettBestilt has not approved the partner yet (all keys). `INSUFFICIENT_SCOPE`: the restaurant key does not have full access (`error.details.requiredScope` is `"*"`).

- `error` (ErrorBody, required)

### 404 Not Found Error

`RESTAURANT_NOT_FOUND`: the same response whether the slug does not exist, belongs to another partner or does not match the restaurant key.

- `error` (ErrorBody, required)

### 409 Conflict Error

`RESTAURANT_ARCHIVED` (the restaurant is archived) or `DEMO_RESTAURANT` (the demo restaurant cannot be changed through the API), or `CHANGED` (a rare database conflict; read and try again).

- `error` (ErrorBody, required)

### 422 Unprocessable Entity Error

`VALIDATION_FAILED` with `error.details.issues`: e.g. a weekday twice (path `days.1.dayOfWeek`), a time that is not `HH:MM`, or an unknown field such as `expectedUpdatedAt`.

- `error` (ErrorBody, required)

### 429 Too Many Requests Error

`RATE_LIMITED`: more than 120 calls per minute for this partner key, or more than 600 per minute for the partner across all its keys (reads and writes). Writes also share a bucket of 30 changes per minute per partner and environment. See `Retry-After`.

- `error` (ErrorBody, required)

## Types

### PartnerOpeningDay

A window across midnight (e.g. 23:00–02:00) only applies to that day's own row: it means open 00:00–02:00 and 23:00–24:00 on the same weekday, not into the next day, so a place open past midnight must also give the next day a row that opens at 00:00. An open day (or override) cannot have `opensAt` equal to `closesAt` (422 `VALIDATION_FAILED`); use 00:00–23:59 for the whole day.

- `dayOfWeek` (enum, required)
  - Allowed values: `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, `SATURDAY`, `SUNDAY`
- `opensAt` (string, required) — Opens, Oslo time.
- `closesAt` (string, required) — Closes, Oslo time. Before `opensAt` means open 00:00–`closesAt` and `opensAt`–24:00 on the same weekday (only that day's row is used), not a window into the next day.
- `isClosed` (boolean, required) — `true` = closed the whole day (the times are not used).

### PartnerHours

- `days` (list of PartnerOpeningDay, required) — The weekly hours, Monday first. A weekday that is missing is closed.
- `overrides` (list of PartnerHoursOverride, required) — All overrides, including past ones, oldest `startDate` first.

### ErrorBody

- `code` (enum, required) — A machine-readable error code.
  - Allowed values: `MISSING_SLUG`, `MISSING_TOKEN`, `INVALID_JSON`, `INVALID_SUCCESS_URL`, `INVALID_CANCEL_URL`, `VALIDATION_FAILED`, `RESTAURANT_NOT_FOUND`, `ORDER_NOT_FOUND`, `RATE_LIMITED`, `BAD_REQUEST`, `UNAUTHORIZED`, `FORBIDDEN`, `INSUFFICIENT_SCOPE`, `KEY_EXPIRED`, `POPUP_NOT_AVAILABLE`, `PRICE_CHANGED`, `PAYMENT_INCOMPLETE`, `PAYMENT_PENDING`, `PAYMENT_PROVIDER_INVALID`, `FULFILLMENT_NOT_ALLOWED`, `IDEMPOTENCY_CONFLICT`, `DINE_IN_DISABLED`, `PARTY_TOO_LARGE`, `TOO_EARLY`, `NO_TABLE`, `INVALID_PARTY_SIZE`, `INTERNAL_ERROR`, `PARTNER_AGREEMENT_REQUIRED`, `IDEMPOTENCY_KEY_REQUIRED`, `SLUG_TAKEN`, `SANDBOX_LIMIT`, `RETENTION`, `RESTAURANT_ARCHIVED`, `KEY_NOT_FOUND`, `KEY_IN_USE`, `WEBHOOK_NOT_FOUND`, `WEBHOOK_LIMIT`, `CHANGED`, `PDF_TOO_LARGE`, `PDF_INVALID`, `PDF_PASSWORD`, `PDF_TOO_MANY_PAGES`, `PDF_NO_TEXT`, `PDF_PARSE_TIMEOUT`, `MENU_IMPORT_EMPTY`, `FILE_UNSUPPORTED`, `IMAGE_HEIC`, `IMAGE_INVALID`, `IMAGE_DIMENSIONS`, `MENU_IMPORT_UNAVAILABLE`, `MENU_IMPORT_AI_LIMIT`, `MENU_IMPORT_TOO_BIG`, `MENU_IMPORT_NOT_FOUND`, `MENU_IMPORT_CLOSED`, `PARTNER_NOT_APPROVED`, `PARTNER_DATA_LOCKED`, `DOMAIN_NOT_FOUND`, `DOMAIN_TAKEN`, `DOMAIN_LIMIT`, `DOMAIN_MANAGED`, `DOMAIN_NOT_VERIFIED`, `OWNER_EXISTS`, `OWNER_EMAIL_NOT_ALLOWED`, `OWNER_INVITE_NOT_FOUND`, `SANDBOX_NOT_SUPPORTED`, `INVITE_SEND_FAILED`, `GO_LIVE_NOT_REQUIRED`, `GO_LIVE_NOT_READY`, `GO_LIVE_CHECKS_FAILED`, `GO_LIVE_ALREADY_REQUESTED`, `GO_LIVE_ALREADY_APPROVED`, `GO_LIVE_NOTHING_TO_WITHDRAW`, `DEMO_RESTAURANT`, `LEGAL_LOCKED`, `HOURS_OVERRIDE_NOT_FOUND`, `HOURS_OVERRIDE_LIMIT`, `DELIVERY_ZONE_NOT_FOUND`, `DELIVERY_ZONE_LIMIT`, `MENU_NOT_FOUND`, `MENU_IS_DEFAULT`, `CATEGORY_NOT_FOUND`, `PRODUCT_NOT_FOUND`, `ADDON_GROUP_NOT_FOUND`, `CATEGORY_NOT_EMPTY`, `CATEGORY_LIMIT`, `PRODUCT_LIMIT`, `ADDON_GROUP_LIMIT`, `KEY_ROLE_FORBIDDEN`, `WHITE_LABEL_REQUIRED`, `LOGO_NOT_ALLOWED`, `FILE_TOO_LARGE`, `UPLOAD_FAILED`, `PARTNER_ARCHIVED`, `PARTNER_NEWS_FORBIDDEN`, `NEWS_NOT_FOUND`, `NOT_DRAFT`, `NOT_PUBLISHED`, `NOT_SCHEDULED`, `NOT_CLAIMABLE`, `NO_RECIPIENTS`, `IMAGE_NOT_ALLOWED`, `SENDER_DOMAIN_ALREADY_SET`, `SENDER_DOMAIN_NOT_SET`, `DOMAIN_EXISTS`, `SENDER_DOMAIN_REJECTED`, `EMAIL_SERVICE_UNAVAILABLE`, `HUMAN_REQUIRED`, `LENGTH_REQUIRED`, `PAYLOAD_TOO_LARGE`
- `message` (string, required) — A human-readable explanation, often in Norwegian. For business rules it is usually suitable to show the customer.
- `requestId` (string, required) — The same value as the `X-Request-Id` response header. Search for it in the API log in LettBestilt Partner, and include it when you contact LettBestilt.
- `details` (any, optional) — Extra data for some codes, for example `issues` for `VALIDATION_FAILED`, the totals for `PRICE_CHANGED` or `requiredScope` for `INSUFFICIENT_SCOPE`.

### PartnerHoursOverride

An override of the weekly hours for a date period (a public holiday, a holiday, summer hours). Dates are `YYYY-MM-DD` in Oslo time; `endDate` is inclusive and cannot be before `startDate`. `isClosed: true` closes the whole period; an open override needs both `opensAt` and `closesAt`. `closesAt` before `opensAt` means open 00:00–`closesAt` and `opensAt`–24:00 on each day of the period (e.g. 18:00/02:00 = 00:00–02:00 and 18:00–24:00 on the same day), not a window into the next day. When several overrides cover the same day, the one with the shortest period wins.

- `id` (string, required)
- `label` (string, required)
- `startDate` (date, required) — Calendar day in Oslo time, `YYYY-MM-DD`.
- `endDate` (date, required) — Calendar day in Oslo time, `YYYY-MM-DD`, inclusive.
- `opensAt` (string, required, nullable) — `null` when the period is closed, or when it is only a label (the weekly hours then apply).
- `closesAt` (string, required, nullable)
- `isClosed` (boolean, required)
- `createdAt` (datetime, required)
- `updatedAt` (datetime, required) — Send it as `expectedUpdatedAt` in PATCH to detect concurrent changes.
- `textCleaned` (integer, optional) — Only in the response to a write, and only when `label` was cleaned: links, email addresses, HTML, `javascript:` or invisible direction marks were removed (as for the menu).

## Examples

**Request**

```json
{
  "days": [
    {
      "dayOfWeek": "MONDAY",
      "opensAt": "15:00",
      "closesAt": "22:00",
      "isClosed": false
    },
    {
      "dayOfWeek": "SATURDAY",
      "opensAt": "13:00",
      "closesAt": "23:00",
      "isClosed": false
    }
  ]
}
```

**Response**

```json
{
  "data": {
    "days": [
      {
        "dayOfWeek": "MONDAY",
        "opensAt": "11:00",
        "closesAt": "11:00",
        "isClosed": true
      }
    ],
    "overrides": [
      {
        "id": "string",
        "label": "Julaften",
        "startDate": "2026-12-24",
        "endDate": "2026-12-24",
        "opensAt": "11:00",
        "closesAt": "11:00",
        "isClosed": true,
        "createdAt": "2024-01-15T09:30:00Z",
        "updatedAt": "2024-01-15T09:30:00Z",
        "textCleaned": 1
      }
    ]
  }
}
```

**SDK Code**

```python Partner_setPartnerRestaurantHours_example
import requests

url = "https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours"

payload = { "days": [
        {
            "dayOfWeek": "MONDAY",
            "opensAt": "15:00",
            "closesAt": "22:00",
            "isClosed": False
        },
        {
            "dayOfWeek": "SATURDAY",
            "opensAt": "13:00",
            "closesAt": "23:00",
            "isClosed": False
        }
    ] }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

response = requests.put(url, json=payload, headers=headers)

print(response.json())
```

```javascript Partner_setPartnerRestaurantHours_example
const url = 'https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours';
const options = {
  method: 'PUT',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"days":[{"dayOfWeek":"MONDAY","opensAt":"15:00","closesAt":"22:00","isClosed":false},{"dayOfWeek":"SATURDAY","opensAt":"13:00","closesAt":"23:00","isClosed":false}]}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Partner_setPartnerRestaurantHours_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours"

	payload := strings.NewReader("{\n  \"days\": [\n    {\n      \"dayOfWeek\": \"MONDAY\",\n      \"opensAt\": \"15:00\",\n      \"closesAt\": \"22:00\",\n      \"isClosed\": false\n    },\n    {\n      \"dayOfWeek\": \"SATURDAY\",\n      \"opensAt\": \"13:00\",\n      \"closesAt\": \"23:00\",\n      \"isClosed\": false\n    }\n  ]\n}")

	req, _ := http.NewRequest("PUT", url, payload)

	req.Header.Add("Authorization", "Bearer <token>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Partner_setPartnerRestaurantHours_example
require 'uri'
require 'net/http'

url = URI("https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Put.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"days\": [\n    {\n      \"dayOfWeek\": \"MONDAY\",\n      \"opensAt\": \"15:00\",\n      \"closesAt\": \"22:00\",\n      \"isClosed\": false\n    },\n    {\n      \"dayOfWeek\": \"SATURDAY\",\n      \"opensAt\": \"13:00\",\n      \"closesAt\": \"23:00\",\n      \"isClosed\": false\n    }\n  ]\n}"

response = http.request(request)
puts response.read_body
```

```java Partner_setPartnerRestaurantHours_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.put("https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"days\": [\n    {\n      \"dayOfWeek\": \"MONDAY\",\n      \"opensAt\": \"15:00\",\n      \"closesAt\": \"22:00\",\n      \"isClosed\": false\n    },\n    {\n      \"dayOfWeek\": \"SATURDAY\",\n      \"opensAt\": \"13:00\",\n      \"closesAt\": \"23:00\",\n      \"isClosed\": false\n    }\n  ]\n}")
  .asString();
```

```php Partner_setPartnerRestaurantHours_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('PUT', 'https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours', [
  'body' => '{
  "days": [
    {
      "dayOfWeek": "MONDAY",
      "opensAt": "15:00",
      "closesAt": "22:00",
      "isClosed": false
    },
    {
      "dayOfWeek": "SATURDAY",
      "opensAt": "13:00",
      "closesAt": "23:00",
      "isClosed": false
    }
  ]
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Partner_setPartnerRestaurantHours_example
using RestSharp;

var client = new RestClient("https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours");
var request = new RestRequest(Method.PUT);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"days\": [\n    {\n      \"dayOfWeek\": \"MONDAY\",\n      \"opensAt\": \"15:00\",\n      \"closesAt\": \"22:00\",\n      \"isClosed\": false\n    },\n    {\n      \"dayOfWeek\": \"SATURDAY\",\n      \"opensAt\": \"13:00\",\n      \"closesAt\": \"23:00\",\n      \"isClosed\": false\n    }\n  ]\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Partner_setPartnerRestaurantHours_example
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["days": [
    [
      "dayOfWeek": "MONDAY",
      "opensAt": "15:00",
      "closesAt": "22:00",
      "isClosed": false
    ],
    [
      "dayOfWeek": "SATURDAY",
      "opensAt": "13:00",
      "closesAt": "23:00",
      "isClosed": false
    ]
  ]] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://www.lettbestilt.no/api/v1/partner/restaurants/slug/hours")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```