# Create site

`POST /api/configuration/sites`

- **Success:** `201 Created`
- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Create site](https://support.gladstonesoftware.cloud/reference/configuration/create-site)

## Overview

Creates a site — a physical centre, branch or venue. A site is the fundamental scoping unit of the platform: activities, prices, memberships, bookings, bank accounts and system configuration all hang off a site id, so this is usually one of the first calls you make when setting up an account.

The site id is a string of up to 15 characters and **you normally choose it yourself** by sending `id`. Leave `id` out and the platform allocates the next one in the `SITE` sequence — the highest existing id beginning with `SITE`, incremented, zero-padded to 15 characters, giving values such as `SITE00000000001`.

**The response has no body.** On success you get a bare `201 Created` with no payload and no `Location` header, so a call that omitted `id` gives you no way to learn the id that was allocated — you have to follow up with [Retrieve all sites](get-all-sites-with-optional-filtering.md) and look for it. If you need to know the id, send your own.

Retrying is safe in the sense that it will not silently duplicate: site ids are unique, so a second call carrying the same `id` returns `409 Conflict` with `Site ID: {id} already exists`. A retry that omits `id` is a different matter — it allocates a fresh sequence value and creates a second site.

Creating a site also sets up its system configuration, which is why a new site is immediately usable rather than blank:

- Around 330 site-level settings are set to **fixed platform defaults**, not copied from your account. Among them are `DefaultTimeBand` = `STAND`, `DefaultSubTypeGroup` = `MISC`, `DefaultPriceLevel` = `STAND`, `DefaultSiteGroup` = `ALL`, `DefaultPaymentMethod` = `CASH`, `DefaultCalendar` = `STAND` and `DefaultTaxCode` = `STAND`. If your account does not use those codes, the new site points at settings that do not exist until you change them. Read and change the time band and membership type group with [Retrieve site defaults](get-site-defaults.md) and [Set site defaults](set-site-defaults.md).
- Twelve further settings are copied from the head-office record (the site whose id is `HO`) at the moment of creation: `Ad hoc Report Title`, `BankAccRefPrefix`, `Base Currency`, `DatabasePrefix`, `DB Currency`, `LocationForCorrDocs`, `LocationForExportDefns`, `LocationForExports`, `LocationForReportArch`, `LocationForReportMacros`, `LocationForUsrDefRpts` and `SecurityServiceURL`. Anything not set at head office is set to an empty string. This is a one-off copy — changing the head-office value afterwards does not propagate.

## Endpoint

`POST {base_url}/api/configuration/sites`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Request body

`address`, `location` and `contact` are required objects, but every field inside them is optional — send `{}` if you have nothing to put in one. Everything else is optional.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | No | The site id, and the value you will pass as `siteId` to almost every other endpoint. Maximum 15 characters, or the call fails with `The site cannot be more than 15 characters`. **It is upper-cased before being stored**, so send `MYSITE` rather than `mysite` and use the upper-case form everywhere afterwards. Must not already be in use — a clash returns `409`. Watch out for spaces: the duplicate check strips them before comparing but the value is stored with them intact, so `MY SITE` can be accepted alongside an existing `MYSITE` and then never match it. Omit the field (or send an empty string) to have an id allocated from the `SITE` sequence instead, at the cost of not being told what it was. |
| `name` | string | No | The site's public-facing name, and what [Retrieve all sites](get-all-sites-with-optional-filtering.md) returns as `name`. Maximum 255 characters. An empty string is rejected with `Site name cannot be empty`, but the field carries no required check, so omitting it altogether is accepted and creates a site with no name — send one. |
| `legalName` | string | No | The registered legal entity operating the site, where that differs from the trading name — for invoices and statutory documents. Maximum 255 characters. Omitting it leaves it null; nothing falls back to `name`. |
| `vatId` | string | No | The site's VAT registration number, as free text. Maximum 20 characters. Not validated for format or checked against any tax authority. |
| `description` | string | No | A longer description of the site, separate from `name`. Maximum 255 characters. Returned as `description` by the site GET endpoints. |
| `address` | object | Yes | The site's postal address. The object itself must be present — omit it and the call fails with `Address section is required` — but all seven members inside it are optional, so `"address": {}` is valid. |
| `address.addressLine1` | string | No | First line of the postal address. Maximum 100 characters. No lines are mandatory and none are validated against a postal address file. |
| `address.addressLine2` | string | No | Second line of the postal address. Maximum 100 characters. |
| `address.addressLine3` | string | No | Third line of the postal address. Maximum 100 characters. |
| `address.townCity` | string | No | Town or city. Maximum 100 characters. |
| `address.region` | string | No | County, state or region. Maximum 100 characters. |
| `address.postalCode` | string | No | Postcode or ZIP code. Maximum 25 characters. Kept exactly as sent — not normalised, spaced or upper-cased. |
| `address.country` | string | No | Country, as free text. Maximum 100 characters. Not an ISO country code and not validated against a list. |
| `location` | object | Yes | The site's map coordinates, used for distance-based site searches. The object must be present — omit it and the call fails with `Location section is required` — but both members are optional, so `"location": {}` is valid. |
| `location.latitude` | string | No | Latitude in decimal degrees, sent as a **string**, for example `"51.4816"`. Maximum 40 characters. Not parsed or range-checked on the way in, so a malformed value is accepted here and only causes trouble later, wherever the coordinate is used. |
| `location.longitude` | string | No | Longitude in decimal degrees, sent as a **string**, for example `"-3.1791"`. Maximum 40 characters, and subject to the same lack of validation as `latitude`. |
| `contact` | object | Yes | The site's public contact details. The object must be present — omit it and the call fails with `Contact section is required` — but both members are optional, so `"contact": {}` is valid. |
| `contact.telephone` | string | No | Public telephone number as free text. Maximum 100 characters. No format validation, so send it in whatever form you want displayed. |
| `contact.email` | string | No | Public contact email address for the site. Maximum 255 characters. Length is the only check — the value is not validated as an email address. |
| `websiteUrl` | string | No | The site's own web page. Maximum 255 characters. Stored and returned verbatim; not fetched or validated, so include the scheme yourself. |
| `logoUrl` | string | No | Absolute URL of the site's logo image. Maximum 255 characters. Nothing is uploaded or resized by this call — host the image yourself and pass the link. |
| `termsConditionsUrl` | string | No | Link to the terms and conditions that apply at this site. Maximum 255 characters. |
| `privacyPolicyUrl` | string | No | Link to the privacy policy that applies at this site. Maximum 255 characters. |
| `openActiveTaxMode` | string | No | Stored verbatim against the site and returned by the site GET endpoints. Maximum 100 characters, or the call fails with `Open active tax mode cannot be more than 100 characters`. |
| `isActive` | boolean | No | Whether the site is in service. **Defaults to `true`** when omitted — unlike most flags on this API — so a site you create is live unless you explicitly send `false`. Send `false` to stage a site before opening it, then switch it on with [Update site](update-site.md). The field is not nullable: sending `null` is rejected as malformed JSON with a `400`. |
| `isWebVisible` | boolean | No | Whether the site is shown in consumer-facing web and app channels. Independent of `isActive` — a site can be active for staff yet hidden from the public. Defaults to `false` when omitted or sent as `null`, so a new site is invisible online until you set it to `true`. |
| `openingDate` | string (date-time) | No | The date the site opens to the public. Only the date part is kept; the time is discarded. Must be today or later — a past date is rejected with `Site opening date cannot be in the past.`, which also means you cannot backdate a historical site through this endpoint. Omit it to leave the site with no opening date, which is accepted. |
| `amenities` | array of object | No | The amenities available at the site — showers, parking and so on. Omit the array or send `[]` for none. Every id must exist and no id may appear twice; either fault rejects the whole request and no site is created. |
| `amenities[].id` | integer (int32) | No | An amenity identifier from [Retrieve all site amenities](retrieves-all-site-amenities-with-their-configuration-details.md). Amenities are reference data and are not created for you here. |
| `facilities` | array of object | No | The facilities the site has — pool, sports hall and so on. Omit the array or send `[]` for none. As with amenities, an unknown or repeated id rejects the whole request. |
| `facilities[].id` | integer (int32) | No | A facility identifier from [Retrieve all site facilities](retrieves-all-site-facilities-with-their-configuration-details.md). |
| `organisations` | array of object | No | The organisations that run this site, linking it into your operator hierarchy for reporting. Omit the array or send `[]` for none. An unknown or repeated id rejects the whole request. |
| `organisations[].id` | integer (int32) | No | An organisation identifier from [Retrieve all organisations](retrieves-all-organizations-configured-in-the-system.md). Create the organisation first if it does not exist. |

## Sample request

```bash title="cURL"
curl -X POST '{base_url}/api/configuration/sites' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": "CARDIFF",
  "name": "Cardiff Leisure Centre",
  "legalName": "Cardiff Leisure Trust Ltd",
  "vatId": "GB123456789",
  "description": "Eight-lane pool, gym and sports hall",
  "address": {
    "addressLine1": "12 Wood Street",
    "addressLine2": "",
    "addressLine3": "",
    "townCity": "Cardiff",
    "region": "South Glamorgan",
    "postalCode": "CF10 1EX",
    "country": "United Kingdom"
  },
  "location": {
    "latitude": "51.4763",
    "longitude": "-3.1795"
  },
  "contact": {
    "telephone": "029 2000 0000",
    "email": "cardiff@example.com"
  },
  "websiteUrl": "https://example.com/cardiff",
  "logoUrl": "https://example.com/cardiff/logo.png",
  "termsConditionsUrl": "https://example.com/terms",
  "privacyPolicyUrl": "https://example.com/privacy",
  "isActive": true,
  "isWebVisible": true,
  "openingDate": "2026-09-01T00:00:00Z",
  "amenities": [
    {
      "id": 3
    }
  ],
  "facilities": [
    {
      "id": 7
    }
  ],
  "organisations": [
    {
      "id": 1
    }
  ]
}'
```

## Response fields

**This endpoint returns no response body.** A successful call is a bare `201 Created` with an empty payload and no `Location` header.

The two fields in the table below appear against this endpoint in the published schema, but they are the shape of [Retrieve site defaults](get-site-defaults.md) (`GET /api/configuration/sites/{siteId}/defaults`), not of this call. Do not write code that reads them from this response.

| Field | Type | Description |
|-------|------|-------------|
| `timebandId` | string | Never returned by this endpoint. It belongs to [Retrieve site defaults](get-site-defaults.md), where it is the site's `DefaultTimeBand` setting — set to `STAND` when the site is created. |
| `membershipTypeGroupId` | string | Never returned by this endpoint. It belongs to [Retrieve site defaults](get-site-defaults.md), where it is the site's `DefaultSubTypeGroup` setting — set to `MISC` when the site is created. |

## Sample response

```text title="201 Created"
(no response body)
```

## Errors

Validation is all-or-nothing: whichever check fails, no site is created and no configuration is saved.

| Status | Meaning | When |
|--------|---------|------|
| `400` | Required section missing | `Address section is required`, `Location section is required` or `Contact section is required`. These are returned as a validation problem document (`application/problem+json`) with an `errors` map keyed by field name, listing every offending field at once. |
| `400` | Field too long | The length-limit message for the field, for example `The site cannot be more than 15 characters`, `Site name cannot be more than 255 characters`, `PostalCode cannot be more than 25 characters` or `Open active tax mode cannot be more than 100 characters`. Also returned as a validation problem document. |
| `400` | Empty site name | `Site name cannot be empty` — `name` was sent as an empty string. Omitting the field entirely passes validation instead. |
| `400` | Opening date in the past | `Site opening date cannot be in the past.` — `openingDate` is earlier than today in UTC. |
| `400` | Unknown amenity, facility or organisation | `Unable to create a site with Amenities as the Amenity ID(s) {ids} do not exist`, or the equivalent `…with Facilities as the Facility ID(s) {ids} do not exist` and `…with Organisations as the Organisation ID(s) {ids} do not exist`. The message lists every id that could not be matched. |
| `400` | Duplicate ids in a list | `Unable to create site as Amenities with ID {ids} are duplicated`, or the equivalent for `Facilities` and `Organisations`. Repeating the same id within one array is rejected rather than de-duplicated. |
| `400` | Malformed JSON | `isActive` is not nullable and `openingDate` must be a date — sending `null` for `isActive`, or an unparseable date, is rejected as malformed JSON before validation runs. Omit a field rather than nulling it. |
| `409` | Site id already in use | `Site ID: {id} already exists`. Also returned as `This entry already exists in the database` if the clash is caught at the moment the site is saved rather than by the initial check. Sites are never overwritten by this endpoint — use [Update site](update-site.md). |
| `403` | Forbidden | The access token does not satisfy this endpoint's requirements — the `FOH` product and the `FOHGENERIC` feature — or its scope is neither `api` nor `partner`. |
| `500` | Internal server error | An unexpected server-side failure while saving the site or its configuration. The body is empty. |
