> 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.

# Use the domain in tracking links

PATCH https://www.lettbestilt.no/api/v1/partner/restaurants/{slug}/domains/{domainId}
Content-Type: application/json

**Partner key + restaurant key.** With `{ "primary": true }` the domain becomes the one LettBestilt's tracking links in emails and on receipts go to (the same as "Use in tracking links" in the partner portal). Verified domains only. The previous primary domain loses the mark in the same operation. If nothing changes you get `200` without a change. Not allowed for an archived restaurant.

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

## Authentication

- `Authorization` header (bearer token, required) — The partner key, sent as `Authorization: Bearer lbp_test_…`. Created in LettBestilt Partner under **Partner key**. It belongs to the 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`. A test key sees and creates only sandboxes; live partner keys are not available yet. A revoked key, a restaurant key (`lbs_…`) here, or a suspended or archived partner returns `401 UNAUTHORIZED`; an expired key returns `401 KEY_EXPIRED`.
- `X-LettBestilt-Restaurant-Key` header (required) — The restaurant's own key (`lbs_test_…` or `lbs_live_…`), sent in `X-LettBestilt-Restaurant-Key` together with the partner key for calls about one restaurant. 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 (otherwise `401 UNAUTHORIZED`). A slug 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.
- `domainId` (string, required) — The domain's `id`.

### Body (application/json)

This endpoint expects an object.

- `primary` (any, required)

## Response

### 200

The domain as it is now.

- `data` (PartnerDomain, required) — Et domene restaurantens nettside ligger på. Et bekreftet domene godtas for kall fra nettleseren (CORS og nettleser-auth) og som successUrl/cancelUrl.

## Errors

### 400 Bad Request Error

`INVALID_JSON`: the body is not valid JSON.

- `error` (ErrorBody, required)

### 401 Unauthorized Error

`UNAUTHORIZED`: a key is missing, invalid or revoked, the two keys are in different environments, or the partner is suspended or archived. `KEY_EXPIRED`: one of the keys is past its expiry date.

- `error` (ErrorBody, required)

### 403 Forbidden Error

`FORBIDDEN`: the request has an `Origin` header. `PARTNER_AGREEMENT_REQUIRED`: live key, but the partner agreements are not signed. `PARTNER_NOT_APPROVED`: LettBestilt has not approved the partner yet. `INSUFFICIENT_SCOPE`: the restaurant key does not have full access.

- `error` (ErrorBody, required)

### 404 Not Found Error

`RESTAURANT_NOT_FOUND`, or `DOMAIN_NOT_FOUND` when the domain does not belong to the restaurant.

- `error` (ErrorBody, required)

### 409 Conflict Error

`RESTAURANT_ARCHIVED`: the restaurant is archived. `CHANGED`: changed at the same time; try again.

- `error` (ErrorBody, required)

### 422 Unprocessable Entity Error

`VALIDATION_FAILED`: the body must be exactly `{ "primary": true }`. `DOMAIN_NOT_VERIFIED`: the domain is not verified yet.

- `error` (ErrorBody, required)

### 429 Too Many Requests Error

`RATE_LIMITED`: more than 120 calls per minute for this partner key, or more than 30 changes per minute for the partner.

- `error` (ErrorBody, required)

## Types

### PartnerDomain

Et domene restaurantens nettside ligger på. Et bekreftet domene godtas for kall fra nettleseren (CORS og nettleser-auth) og som successUrl/cancelUrl.

- `id` (string, required)
- `domain` (string, required) — Normalisert vertsnavn (små bokstaver, ASCII/punycode), f.eks. `www.restauranten.no`.
- `primary` (boolean, required)
- `verified` (boolean, required)
- `verifiedAt` (datetime, required, nullable)
- `managedByLettBestilt` (boolean, required) — true når LettBestilt styrer domenet. Det kan da ikke fjernes via API-et (`DOMAIN_MANAGED`).
- `verification` (PartnerDomainVerification, required, nullable) — TXT-posten som beviser eierskap, så lenge domenet ikke er bekreftet. null når det er bekreftet.
- `lastCheckedAt` (datetime, required, nullable) — Siste forsøk på å bekrefte domenet.

### ErrorBody

- `code` (enum, required) — A machine-readable error code. The list comes from the platform spec, which a test keeps in step with the code. See the Errors guide for what each code means.
  - 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`
- `message` (string, required) — A human-readable explanation. Often in Norwegian, and for business rules 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`.

### PartnerDomainVerification

TXT-posten som beviser eierskap, så lenge domenet ikke er bekreftet. null når det er bekreftet.

- `type` (any, required)
- `name` (string, required) — `_lettbestilt-verify.<domene>`.
- `value` (string, required)

## Examples

**Request**

```json
{
  "primary": null
}
```

**Response**

```json
{
  "data": {
    "id": "string",
    "domain": "string",
    "primary": true,
    "verified": true,
    "verifiedAt": "2024-01-15T09:30:00Z",
    "managedByLettBestilt": true,
    "verification": {
      "type": null,
      "name": "string",
      "value": "string"
    },
    "lastCheckedAt": "2024-01-15T09:30:00Z"
  }
}
```

**SDK Code**

```python Partner_setRestaurantPrimaryDomain_example
import requests

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

payload = { "primary": None }
headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
}

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

print(response.json())
```

```javascript Partner_setRestaurantPrimaryDomain_example
const url = 'https://www.lettbestilt.no/api/v1/partner/restaurants/slug/domains/domainId';
const options = {
  method: 'PATCH',
  headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
  body: '{"primary":null}'
};

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

```go Partner_setRestaurantPrimaryDomain_example
package main

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

func main() {

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

	payload := strings.NewReader("{\n  \"primary\": null\n}")

	req, _ := http.NewRequest("PATCH", 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_setRestaurantPrimaryDomain_example
require 'uri'
require 'net/http'

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

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

request = Net::HTTP::Patch.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"primary\": null\n}"

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

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

HttpResponse<String> response = Unirest.patch("https://www.lettbestilt.no/api/v1/partner/restaurants/slug/domains/domainId")
  .header("Authorization", "Bearer <token>")
  .header("Content-Type", "application/json")
  .body("{\n  \"primary\": null\n}")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('PATCH', 'https://www.lettbestilt.no/api/v1/partner/restaurants/slug/domains/domainId', [
  'body' => '{
  "primary": null
}',
  'headers' => [
    'Authorization' => 'Bearer <token>',
    'Content-Type' => 'application/json',
  ],
]);

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

```csharp Partner_setRestaurantPrimaryDomain_example
using RestSharp;

var client = new RestClient("https://www.lettbestilt.no/api/v1/partner/restaurants/slug/domains/domainId");
var request = new RestRequest(Method.PATCH);
request.AddHeader("Authorization", "Bearer <token>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"primary\": null\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Partner_setRestaurantPrimaryDomain_example
import Foundation

let headers = [
  "Authorization": "Bearer <token>",
  "Content-Type": "application/json"
]
let parameters = ["primary": ] 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/domains/domainId")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "PATCH"
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()
```