# Accessing Squarespace APIs using OAuth 2.0

Squarespace uses <a href="https://tools.ietf.org/html/rfc6749" target="_blank">OAuth 2.0</a> to authorize third-party applications to integrate with Squarespace and consume Squarespace APIs.

## Terms

<table>
  <tbody>
    <tr>
      <td>
        <strong>Client</strong>
      </td>
      <td>Third-party application that needs to make Squarespace API calls.</td>
    </tr>
    <tr>
      <td>
        <strong>User</strong>
      </td>
      <td>
        Squarespace customer who wants to integrate their site with a client.
      </td>
    </tr>
    <tr>
      <td>
        <strong>Confirmation page</strong>
      </td>
      <td>
        Page created by Squarespace for the user to authorize (<strong>Allow</strong> or <strong>Deny</strong>) client access to their Squarespace account.
      </td>
    </tr>
    <tr>
      <td>
        <strong>Access token</strong>
      </td>
      <td>Short-term token to make authenticated API requests.</td>
    </tr>
    <tr>
      <td>
        <strong>Refresh token</strong>
      </td>
      <td>Long-term token to obtain new access tokens.</td>
    </tr>
  </tbody>
</table>

## Instructions

### 1. Register the client and obtain OAuth 2.0 credentials

Before a client can make Squarespace API calls, the client must be registered with Squarespace as an OAuth client. Start the Oauth client process [here](https://account.squarespace.com/developer-apps).

- Client name
- Icon image (.png or .svg format, max size 200kb, 1:1 ratio)
- Redirect URI(s) - For example, `https://thirdpartyapp.com/oauth/connect`
- Initiate URL\* - For example, `https://thirdpartyapp.com/oauth/initiate`.
- Link to Client's Terms and Conditions
- Link to Client's Privacy Policy

\*Applies only to Squarespace partner apps. These will be linked from inside Squarespace.

We will review your registration and respond with `your_client_id` and `your_client_secret` as soon as possible. These credentials are used in the subsequent steps.

### 2. Prompt the user to authorize client access

The `/authorize` URL prompts the user to authorize client access to their Squarespace data and serves as the confirmation page. If a user selects **Allow**, OAuth 2.0 authentication is started between the client and Squarespace APIs. Append required and any optional parameters to the `GET /authorize` endpoint; **all parameter values should be URL-encoded**.

```
GET https://login.squarespace.com/api/1/login/oauth/provider/authorize
```

<br />
<table>
  <thead>
    <tr>
      <th>Query parameter</th>
      <th>Requirement</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>`client_id`</td>
      <td>Required</td>
      <td>
        Alphanumeric value for your_client_id that was supplied by
        Squarespace in <a href="#1-register-the-client-and-obtain-oauth-20-credentials">Step 1</a>.
      </td>
    </tr>
    <tr>
      <td>`redirect_uri`</td>
      <td>Required</td>
      <td>String; the full redirect URI sent to Squarespace in <a href="#1-register-the-client-and-obtain-oauth-20-credentials">Step 1</a>.</td>
    </tr>
    <tr>
      <td>`scope`</td>
      <td>Required</td>
      <td>
        String; comma-separated list of client permission values (see below)
        for API access. The confirmation page always displays website(s) for
        user selection for `scope=website.*`
        <br />
        <br />
        <table>
          <thead></thead>
          <tbody>
            <tr>
              <td>
                `website.orders`
              </td>
              <td>Send order data and mark orders as fulfilled.</td>
            </tr>
            <tr>
              <td>
                `website.orders.read`
              </td>
              <td>View order and fulfillment information.</td>
            </tr>
            <tr>
              <td>
                `website.transactions.read`
              </td>
              <td>Access transactional order and donation data.</td>
            </tr>
            <tr>
              <td>
                `website.inventory`
              </td>
              <td>View and update inventory stock levels.</td>
            </tr>
            <tr>
              <td>
                `website.inventory.read`
              </td>
              <td>View inventory stock levels.</td>
            </tr>
            <tr>
              <td>
                `website.products`
              </td>
              <td>View product information and modify products.</td>
            </tr>
            <tr>
              <td>
                `website.products.read`
              </td>
              <td>View product information.</td>
            </tr>
            <tr>
              <td>
                `website.contacts`
              </td>
              <td>View customer contact information and address book entries; create, update, and delete contacts and address book entries.</td>
            </tr>
            <tr>
              <td>
                `website.contacts.read`
              </td>
              <td>View customer contact information and address book entries.</td>
            </tr>
            <tr>
              <td>
                `website.discounts`
              </td>
              <td>View and manage discounts.</td>
            </tr>
            <tr>
              <td>
                `website.discounts.read`
              </td>
              <td>View discounts.</td>
            </tr>
          </tbody>
        </table>
        <br />
        <strong>Example:</strong>
        <br />
        `&scope=website.inventory,website.orders`
      </td>
    </tr>
    <tr>
      <td>`state`</td>
      <td>Required</td>
      <td>
        Alphanumeric random value generated by client. You'll use it in
        Step 3, <a href="#3-implement-redirect-uri">Implement redirect URI</a>, to prevent CSRF attacks.
      </td>
    </tr>
    <tr>
      <td>`website_id`</td>
      <td>Conditionally Required</td>
      <td>
        Alphanumeric value for a Squarespace site ID.
        <br />
        When a logged-in user initiates an OAuth connection from Squarespace, this
        value is appended as a query parameter on the provided initiate URL. Partners must
        pass this parameter back to the `GET /authorize` endpoint.
        <br />
        If an OAuth connection is initiated by a logged-out user, Squarespace will not
        include a website_id parameter on the initiate URL. Partners should not pass any
        `website_id` parameter back to the `GET /authorize`
        endpoint in these cases. Instead, the user will be prompted to select a website
        on the confirmation page.
      </td>
    </tr>
    <tr>
      <td>`access_type`</td>
      <td>Optional</td>
      <td>
        String; use `offline` for long-term API access. For more
        information, see <a href="#token-expiration-and-long-term-access">Token expiration and long-term access</a>. Omit
        parameter for short-term API access.
      </td>
    </tr>

  </tbody>
</table>

**Authorize URL example with parameters**

_Note: Parameter values are in plain text and not URL-encoded for example purposes._

```
https://login.squarespace.com/api/1/login/oauth/provider/authorize?client_id=fGBjMDBaUHli&redirect_uri=http://localhost:8090/oauth/callback&scope=website.inventory,website.orders&state=2BsmGS9AAzFppUmcoIagOLj4iKII
```

### 3. Implement redirect URI

Both **Allow** or **Deny** on the confirmation page directs the user to the client redirect URI (i.e., the `redirect_uri` specified in [Step 2](#2-prompt-the-user-to-authorize-client-access)) with query parameters appended by Squarespace. For **Allow**, the redirect URI should verify the `state` parameter and match it against `state` value that was appended for the `/authorize` endpoint. This verification prevents CSRF attacks. Squarespace doesn't provide guidelines for **Deny** implementation.

<table>
  <thead>
    <tr>
      <th>Parameter</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        `error`
      </td>
      <td>
        String; `access_denied` if user selects <strong>Deny</strong>{" "}
        on confirmation page. Parameter isn't appended if user selects{" "}
        <strong>Allow</strong>.
      </td>
    </tr>
    <tr>
      <td>
        `state`
      </td>
      <td>
        Alphanumeric value; not present if user selects <strong>Deny</strong>.
      </td>
    </tr>
    <tr>
      <td>
        `code`
      </td>
      <td>
        Alphanumeric value used in <a href="#4-request-access-token">step 4. Request access token</a>. Value is
        initially URL-encoded and should be decoded prior to sending in the
        access token request. Valid for two minutes and only for one-time use.
        Not present if user selects <strong>Deny</strong>.
      </td>
    </tr>
  </tbody>
</table>

### 4. Request access token

```
POST https://login.squarespace.com/api/1/login/oauth/provider/tokens
```

The client makes a `POST /tokens` call with required and any optional parameters for an access token from Squarespace. Access tokens in the response are only valid for 30 minutes. For more information, see [Token expiration and long-term access](#token-expiration-and-long-term-access).

**Parameters**

<table>
  <thead>
    <tr>
      <th>Body</th>
      <th>Requirement</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>`grant_type`</td>
      <td>Optional</td>
      <td>
        String; default is `authorization_code`; use `authorization_code` for
        your first request. For <a href="#token-expiration-and-long-term-access">long-term API access</a>, use `refresh_token` for
        all subsequent requests.
      </td>
    </tr>
    <tr>
      <td>`code`</td>
      <td>
        Required if `grant_type=authorization_code`
      </td>
      <td>Alphanumeric value of code passed to redirect URI.</td>
    </tr>
    <tr>
      <td>`redirect_uri`</td>
      <td>
        Required if `grant_type=authorization_code`
      </td>
      <td>String; redirect_uri used for the `/authorize` endpoint.</td>
    </tr>
    <tr>
      <td>`refresh_token`</td>
      <td>
        Required if `grant_type=refresh_token`
      </td>
      <td>String; use application/json or application/x-www-form-urlencoded</td>
    </tr>
  </tbody>
  <thead>
    <tr>
      <th>Header</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>`Authorization`</td>
      <td>Required</td>
      <td>
        Follow the steps below to build the header value:
        <br />
        1. <a href="#1-register-the-client-and-obtain-oauth-20-credentials">Use your Squarespace OAuth 2.0 credentials</a> to construct a string in
        the format: `your_client_id:your_client_secret`<br />
        2. Encode the string from Step 1 into base-64
        <br />
        3. Add Basic&lt;space&gt; as a prefix to the encoded string from Step 2
        <br />
        <br />
        <strong>Example</strong>
        <br />
        your_client_id: abc123
        <br />
        your_client_secret: 12secret345
        <br />
        <br />
        <strong>Authorization after base-64</strong>
        <br />
        Basic YWJjMTIzOjEyc2VjcmV0MzQ1
      </td>
    </tr>
    <tr>
      <td>`Content-Type`</td>
      <td>Required</td>
      <td>String; use application/json or application/x-www-form-urlencoded</td>
    </tr>
    <tr>
      <td>`User-Agent`</td>
      <td>Required</td>
      <td>
        String; If you see an error referencing SEC-43, a User-Agent header is
        missing
      </td>
    </tr>
  </tbody>
</table>

**Example Response**

```javascript
{
  "token_type": "bearer",
  "access_token": "T1|Rr0Wc95uu3cSeBh06yB...",
  // expires 30 minutes after it's created ...
  "access_token_expires_at": "1553532363.542",
}
```

### 5. Make API requests

After you obtain an access token, you can make API requests.

**Endpoint:**

`https://api.squarespace.com/...`

**Authorization format:**

`Bearer<space><access-token-from-tokens-response>`

Using the sample response from Step 4, `Authorization` would have the value:

`Bearer T1|Rr0Wc95uu3cSeBh06yB...`

## Token expiration and long-term access

When a response from `POST /tokens` is received, `access_token` is only valid for 30 minutes. For long-term Squarespace API access, the client has to prompt the user to reauthorize using the `GET /authorize` endpoint or use refresh tokens.

### Refresh tokens

To get a refresh token, add `access_type=offline` as a parameter to the `GET /authorize` request. When the client makes a `POST /tokens` call, the response includes `refresh_token`. Use the refresh token to get new tokens by making another `POST /tokens` call with the parameters below. The refresh token expires after seven days.

- `grant_type=refresh_token`
- `refresh_token=<previous-refresh-token-value>`

_**Note**: Each `POST /tokens` call with a `refresh_token` parameter results in a new `refresh_token` and `access_token`. Refresh tokens are effectively one-time use—the original refresh token is invalidated as soon as the new access token is used. If long-term API access is required, the client should replace the stored refresh token with the most recent `refresh_token`. Note that obtaining new tokens doesn't revoke existing access tokens._

**Sample response from `/tokens`:**

```
{
  "token": "T1|Rr0Wc95uu3cSeBh06yB...",
  "token_type": "bearer",

  // expires 30 mins after creation
  "access_token": "T1|Rr0Wc95uu3cSeBh06yB...",
  "access_token_expires_at": "1553532363.542",

  // expires 7 days after creation
  "refresh_token": "1|KYUYh35zcwzx4Zt/oty3...",
  "refresh_token_expires_at": "1554135363.542"
}
```

**Sample use case with refresh tokens**

Client has to poll orders from a user site indefinitely and needs to call the Squarespace API once
per minute.

1. User authorizes client access to Squarespace APIs via confirmation page; client makes a `POST /tokens` call and obtains access and refresh tokens in response.
2. Client secures and stores the values below for current record (`CR`):

- `access_token=<at-1>`
- `access_token_expires_at=<timestamp>`
- `refresh_token=<rt-1>`

3. Prior to each API call, client checks if:

   `CR.access_token_expires_at - currentTime > 10 seconds`

   If true, client makes an API call with `CR.access_token`.

   If false, client makes a `POST /tokens` call with `refresh_token=CR.refresh_token` as a parameter for new access and refreshntokens; client stores new token values in CR:

   - `access_token=<at-2>`
   - `access_token_expires_at=<timestamp>`
   - `refresh_token=<rt-2>`
4. Client repeats Steps 2 and 3 indefinitely.

### Invalid tokens and revoking access

Tokens become invalid in two ways: the tokens expire or the user revokes client access from the Squarespace UI. In either case, the user has to reauthorize client access to get new access tokens.

When an access token is invalid, Squarespace responds to API requests with `401 Unauthorized`. If a client receives a 401, they should notify the user that their Squarespace integration requires reauthorization.
