> This page is for Developer.

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

# GraphQL Storefront API: Company address book

> Manage a B2B Edition Company's address book with the GraphQL Storefront API — read address settings and extra field definitions, list and filter addresses, get a single address, and add, update, delete, and set default addresses.

BigCommerce's GraphQL Storefront API lets a signed-in B2B Edition user read and manage the address book of their active Company. Use it to list and search the Company's addresses, look up a single address, add, update, and delete addresses, and mark an address as the Company's default shipping or billing address.

All of these operations act on the active Company, which is the Company the signed-in user is currently representing. The API resolves the active Company from the session, so no operation accepts a Company ID. The default Buyer Portal uses these operations to power its **Addresses** page, and you can call them directly when building a custom storefront.

## Tokens

To authenticate calls to the GraphQL Storefront API, your application can generate a bearer token. Use either the [Create a storefront token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/create-token) or the [Create a customer impersonation token](/developer/api-reference/rest/admin/authentication-apis/storefront-api-tokens/customer-impersonation-token/create-token-with-customer-impersonation) REST endpoint. On a Stencil storefront, you can also [access a token](/developer/docs/storefront/guides/graphql-storefront-api) directly from the page context.

Every operation on this page requires [customer context](/developer/docs/storefront/guides/graphql-storefront-api/customer-context): the request must identify a signed-in customer who is a B2B Edition Company user or a Super Admin. Without it, there is no active Company and the address operations return no data.

For more information, see [Storefront tokens](/developer/docs/storefront/guides/graphql-storefront-api/authentication#storefront-tokens) and [Dynamic tokens](/developer/docs/overview/api-fundamentals/api-accounts#bigcommerce-generated-jwts).

## Where the operations live

Reads and writes sit in different places in the schema:

| Operation                                    | Location                                                                       |
| -------------------------------------------- | ------------------------------------------------------------------------------ |
| List addresses                               | `customer.activeCompany.addresses`                                             |
| Get one address                              | `customer.activeCompany.address`                                               |
| Add, update, and delete an address           | `company.addAddress`, `company.updateAddress`, `company.deleteAddress`         |
| Address settings and extra field definitions | `site.settings.b2b.config.address` and `site.settings.b2b.extraFields.address` |

> **Note**
>
> The `company` query field, including `company.addresses` and `company.address`, is deprecated. Use `customer.activeCompany.addresses` and `customer.activeCompany.address` instead. The `company.*` mutations in the table above aren't affected.

## Permissions

What a caller can do depends on the Company role of the signed-in user. B2B Edition ships three predefined roles, and store owners can create custom roles with any combination of permissions.

| Operation                 | Admin | Senior Buyer | Junior Buyer | Custom role                         |
| ------------------------- | ----- | ------------ | ------------ | ----------------------------------- |
| `activeCompany.addresses` | Yes   | Yes          | Yes          | Requires **View** under Addresses   |
| `activeCompany.address`   | Yes   | Yes          | Yes          | Requires **View** under Addresses   |
| `company.addAddress`      | Yes   | No           | No           | Requires **Create** under Addresses |
| `company.updateAddress`   | Yes   | No           | No           | Requires **Edit** under Addresses   |
| `company.deleteAddress`   | Yes   | No           | No           | Requires **Delete** under Addresses |

In the control panel, the create, edit, and delete permissions each require the view permissions, so a custom role that can change addresses can always read them.

A Super Admin who is masquerading as a Company has the same permissions as the predefined Admin role in that Company. A Super Admin who isn't masquerading has no active Company, so `addresses` returns an empty list.

Address changes also depend on the store's address book settings. If the address book is turned off for the store, or address changes aren't allowed for the caller's type of user, the mutations return a `PERMISSION_DENIED` error. Read the settings first, as described in [Get the address settings](#get-the-address-settings), to decide whether to show address management controls.

To learn more about Company roles, see [Company Roles and Permissions](/developer/api-reference/rest/b2b/management/company/company-roles-and-permissions) and the "Company Roles" section of [B2B Edition Settings](https://support.bigcommerce.com/s/article/B2B-Edition-Settings) in the Help Center.

## Get the address settings

Before you render an address list or form, query `site.settings.b2b` for the store's address configuration. Both fields return `null` when B2B address settings aren't available for the store.

* `config.address` returns a list of toggles. Each has a `key` and an `isEnabled` flag. The keys are:

  | Key                 | When `isEnabled` is `true`                                                            |
  | ------------------- | ------------------------------------------------------------------------------------- |
  | `address_book`      | The address book is turned on for the store                                           |
  | `address_admin`     | Company users with the required role permission can add, update, and delete addresses |
  | `address_sales_rep` | A masquerading Super Admin can add, update, and delete addresses                      |
  | `address_label`     | The store uses address labels                                                         |
* `extraFields.address` returns the extra field definitions the store owner has configured for Company addresses. Each definition tells you which fields exist, which are required, and how to validate each value.

Each extra field definition implements the `B2BExtraFieldDefinition` interface. The concrete type tells you which kind of value the field holds and which input list to use when you write it:

| Definition type                        | Value type             | Type-specific fields        | Write with                    |
| -------------------------------------- | ---------------------- | --------------------------- | ----------------------------- |
| `B2BTextExtraFieldDefinition`          | Single-line text       | `maxLength`, `defaultText`  | `extraFields.texts`           |
| `B2BMultilineTextExtraFieldDefinition` | Multiline text         | `rows`, `defaultText`       | `extraFields.multilineTexts`  |
| `B2BNumberExtraFieldDefinition`        | Number                 | `maxValue`, `defaultNumber` | `extraFields.numbers`         |
| `B2BDropdownExtraFieldDefinition`      | One choice from a list | `options`, `defaultValue`   | `extraFields.multipleChoices` |

Use `fieldName` as the `name` when you read or write a value. `labelName` is the display label, and `visibleToEndUser` tells you whether the store owner wants shoppers to see the field at all.

#### Request

**`Example query: Get the address settings`**

```graphql title="Example query: Get the address settings" showLineNumbers={false}
query {
  site {
    settings {
      b2b {
        config {
          address {
            key
            isEnabled
          }
        }
        extraFields {
          address {
            fieldName
            labelName
            isRequired
            visibleToEndUser
            ... on B2BTextExtraFieldDefinition {
              maxLength
              defaultText
            }
            ... on B2BMultilineTextExtraFieldDefinition {
              rows
              defaultText
            }
            ... on B2BNumberExtraFieldDefinition {
              maxValue
              defaultNumber
            }
            ... on B2BDropdownExtraFieldDefinition {
              options
              defaultValue
            }
          }
        }
      }
    }
  }
}
```

#### Response

**`Example query response`**

```json title="Example query response" showLineNumbers={false}
{
  "data": {
    "site": {
      "settings": {
        "b2b": {
          "config": {
            "address": [
              { "key": "address_label", "isEnabled": true },
              { "key": "address_admin", "isEnabled": true },
              { "key": "address_sales_rep", "isEnabled": false },
              { "key": "address_book", "isEnabled": true }
            ]
          },
          "extraFields": {
            "address": [
              {
                "fieldName": "Dock Number",
                "labelName": "Dock number",
                "isRequired": true,
                "visibleToEndUser": true,
                "maxLength": 10,
                "defaultText": null
              },
              {
                "fieldName": "Delivery Window",
                "labelName": "Delivery window",
                "isRequired": false,
                "visibleToEndUser": true,
                "options": ["Morning", "Afternoon", "Evening"],
                "defaultValue": "Morning"
              }
            ]
          }
        }
      }
    }
  }
}
```

## List addresses

The `addresses` field on `customer.activeCompany` returns a paginated list of the addresses in the active Company's address book. It supports [cursor-based pagination](/developer/docs/storefront/guides/graphql-storefront-api#pagination) with the standard `first`, `after`, `last`, and `before` arguments.

Each address reports whether it can be used for shipping or billing (`isShipping`, `isBilling`) and whether it's the Company's default shipping or billing address (`isDefaultShipping`, `isDefaultBilling`).

If B2B Edition isn't enabled for the store, or the address feature isn't available to the store, the list is empty rather than an error. A caller without the permission to view addresses gets a `PERMISSION_DENIED` error instead. See [Handle errors](#handle-errors).

Address extra field values are only fetched when you ask for them. Set `includesExtraFields` to `true` and select `extraFields`, using inline fragments on the `ExtraFieldValue` implementations to read each value's typed field.

#### Request

**`Example query: List the first ten addresses with their extra fields`**

```graphql title="Example query: List the first ten addresses with their extra fields" showLineNumbers={false}
query {
  customer {
    activeCompany {
      addresses(first: 10, includesExtraFields: true) {
        pageInfo {
          hasNextPage
          endCursor
        }
        edges {
          node {
            entityId
            label
            firstName
            lastName
            address1
            address2
            city
            stateOrProvince
            stateOrProvinceCode
            postalCode
            country
            countryCode
            phone
            isShipping
            isBilling
            isDefaultShipping
            isDefaultBilling
            createdAt
            updatedAt
            extraFields {
              name
              fieldEntityId
              ... on TextExtraFieldValue {
                text
              }
              ... on MultilineTextExtraFieldValue {
                multilineText
              }
              ... on NumberExtraFieldValue {
                number
              }
              ... on MultipleChoiceExtraFieldValue {
                value
              }
            }
          }
        }
      }
    }
  }
}
```

#### Response

**`Example query response`**

```json title="Example query response" showLineNumbers={false}
{
  "data": {
    "customer": {
      "activeCompany": {
        "addresses": {
          "pageInfo": {
            "hasNextPage": false,
            "endCursor": "YXJyYXljb25uZWN0aW9uOjE="
          },
          "edges": [
            {
              "node": {
                "entityId": 1,
                "label": "Headquarters",
                "firstName": "Marie",
                "lastName": "Curie",
                "address1": "100 Congress Ave",
                "address2": null,
                "city": "Austin",
                "stateOrProvince": "Texas",
                "stateOrProvinceCode": "TX",
                "postalCode": "78701",
                "country": "United States",
                "countryCode": "US",
                "phone": "5121234567",
                "isShipping": true,
                "isBilling": true,
                "isDefaultShipping": true,
                "isDefaultBilling": true,
                "createdAt": "2026-06-02T14:21:07Z",
                "updatedAt": "2026-08-19T09:03:44Z",
                "extraFields": [
                  { "name": "Dock Number", "fieldEntityId": 21, "text": "D-4" },
                  { "name": "Delivery Window", "fieldEntityId": 22, "value": "Morning" }
                ]
              }
            },
            {
              "node": {
                "entityId": 44,
                "label": "Warehouse",
                "firstName": "Pierre",
                "lastName": "Curie",
                "address1": "1 Bulk St",
                "address2": "Dock 2",
                "city": "Dallas",
                "stateOrProvince": "Texas",
                "stateOrProvinceCode": "TX",
                "postalCode": "75201",
                "country": "United States",
                "countryCode": "US",
                "phone": null,
                "isShipping": true,
                "isBilling": false,
                "isDefaultShipping": false,
                "isDefaultBilling": false,
                "createdAt": "2026-07-11T10:48:55Z",
                "updatedAt": "2026-07-11T10:48:55Z",
                "extraFields": []
              }
            }
          ]
        }
      }
    }
  }
}
```

### Filter and sort addresses

Pass `filters` to narrow the list. All filters are optional and combine with each other.

| Filter              | Type      | Returns                                             |
| ------------------- | --------- | --------------------------------------------------- |
| `searchQuery`       | `String`  | Addresses matching the text in their address fields |
| `isShipping`        | `Boolean` | Addresses enabled for shipping                      |
| `isBilling`         | `Boolean` | Addresses enabled for billing                       |
| `isDefaultShipping` | `Boolean` | The default shipping address                        |
| `isDefaultBilling`  | `Boolean` | The default billing address                         |
| `city`              | `String`  | Addresses in a city                                 |
| `state`             | `String`  | Addresses in a state or province                    |
| `country`           | `String`  | Addresses in a country                              |
| `companyIds`        | `[ID!]`   | Addresses belonging to these Company IDs            |

Pass `sort` with a `field` and a `direction` (`ASC` or `DESC`) to order the results. `CREATED_AT` is the only sort field.

**`Example query: Find shipping addresses in Texas, newest first`**

```graphql title="Example query: Find shipping addresses in Texas, newest first" showLineNumbers={false}
query {
  customer {
    activeCompany {
      addresses(
        first: 10
        filters: { isShipping: true, state: "Texas" }
        sort: [{ field: CREATED_AT, direction: DESC }]
      ) {
        edges {
          node {
            entityId
            label
            address1
            city
          }
        }
      }
    }
  }
}
```

## Get a single address

The `address` field on `customer.activeCompany` returns one address by its `entityId`, or `null` if the Company has no address with that ID. It returns the same fields as the nodes of `addresses`, including `extraFields`. A caller without the permission to view addresses gets a `PERMISSION_DENIED` error instead of `null`.

#### Request

**`Example query: Get an address by ID`**

```graphql title="Example query: Get an address by ID" showLineNumbers={false}
query {
  customer {
    activeCompany {
      address(entityId: 44) {
        entityId
        label
        address1
        address2
        city
        stateOrProvinceCode
        postalCode
        countryCode
        isShipping
        isBilling
        isDefaultShipping
        isDefaultBilling
      }
    }
  }
}
```

#### Response

**`Example query response`**

```json title="Example query response" showLineNumbers={false}
{
  "data": {
    "customer": {
      "activeCompany": {
        "address": {
          "entityId": 44,
          "label": "Warehouse",
          "address1": "1 Bulk St",
          "address2": "Dock 2",
          "city": "Dallas",
          "stateOrProvinceCode": "TX",
          "postalCode": "75201",
          "countryCode": "US",
          "isShipping": true,
          "isBilling": false,
          "isDefaultShipping": false,
          "isDefaultBilling": false
        }
      }
    }
  }
}
```

## Add an address

The `addAddress` mutation on `company` adds an address to the active Company. `firstName`, `lastName`, `address1`, `city`, and `countryCode` are required. `countryCode` is an ISO 3166-1 alpha-2 code, such as `US`. `address2`, `stateOrProvince`, `postalCode`, `phone`, `label`, and `extraFields` are optional, but you must supply a value for every extra field the store has marked as required.

On success, `entityId` holds the ID of the new address and `errors` is empty. If the request fails, `entityId` is `null` and `errors` explains why. Each error has a `message` and an optional machine-readable `code`. See [Handle errors](#handle-errors).

`addAddress` doesn't accept the shipping, billing, or default flags. A new address starts as not-shipping, not-billing, and not-default. To change that, follow up with [`updateAddress`](#update-an-address).

#### Request

**`Example mutation: Add an address with extra fields`**

```graphql title="Example mutation: Add an address with extra fields" showLineNumbers={false}
mutation {
  company {
    addAddress(
      input: {
        label: "Austin Showroom"
        firstName: "Irène"
        lastName: "Joliot-Curie"
        address1: "500 W 2nd St"
        city: "Austin"
        stateOrProvince: "Texas"
        postalCode: "78701"
        countryCode: "US"
        phone: "5125550142"
        extraFields: {
          texts: [{ name: "Dock Number", text: "D-7" }]
          multipleChoices: [{ name: "Delivery Window", fieldValue: "Afternoon" }]
        }
      }
    ) {
      entityId
      errors {
        message
        code
      }
    }
  }
}
```

#### Response

**`Example mutation response`**

```json title="Example mutation response" showLineNumbers={false}
{
  "data": {
    "company": {
      "addAddress": {
        "entityId": 103,
        "errors": []
      }
    }
  }
}
```

## Update an address

The `updateAddress` mutation on `company` changes an existing address. `entityId` identifies the address. Every other input field is optional, so send only the values you want to change. Fields you leave out keep their current values.

On success, `entityId` echoes the updated address's ID and `errors` is empty.

#### Request

**`Example mutation: Change an address's phone number and label`**

```graphql title="Example mutation: Change an address's phone number and label" showLineNumbers={false}
mutation {
  company {
    updateAddress(
      input: {
        entityId: 103
        label: "Austin Flagship"
        phone: "5125550199"
        extraFields: {
          multipleChoices: [{ name: "Delivery Window", fieldValue: "Morning" }]
        }
      }
    ) {
      entityId
      errors {
        message
        code
      }
    }
  }
}
```

#### Response

**`Example mutation response`**

```json title="Example mutation response" showLineNumbers={false}
{
  "data": {
    "company": {
      "updateAddress": {
        "entityId": 103,
        "errors": []
      }
    }
  }
}
```

## Set a default address

The Company's default shipping and default billing addresses are managed through `updateAddress` too. There's no separate mutation. Set `isDefaultShipping` or `isDefaultBilling` to `true` on the address you want to make the default. The two defaults are independent, so one address can be the default for shipping and another the default for billing.

An address can only be a default for a purpose it's enabled for. Setting `isDefaultShipping: true` on an address that isn't a shipping address returns an error, so set `isShipping: true` in the same request when the address isn't already enabled for shipping. The same applies to `isDefaultBilling` and `isBilling`.

#### Request

**`Example mutation: Make an address the default shipping address`**

```graphql title="Example mutation: Make an address the default shipping address" showLineNumbers={false}
mutation {
  company {
    updateAddress(
      input: {
        entityId: 103
        isShipping: true
        isDefaultShipping: true
      }
    ) {
      entityId
      errors {
        message
        code
      }
    }
  }
}
```

#### Response

**`Example mutation response`**

```json title="Example mutation response" showLineNumbers={false}
{
  "data": {
    "company": {
      "updateAddress": {
        "entityId": 103,
        "errors": []
      }
    }
  }
}
```

> **Note**
>
> Because `updateAddress` only changes the fields you send, setting a default doesn't clear the rest of the address.

## Delete an address

The `deleteAddress` mutation on `company` removes an address from the active Company's address book.

On success, `entityId` echoes the deleted address's ID and `errors` is empty.

#### Request

**`Example mutation: Delete an address`**

```graphql title="Example mutation: Delete an address" showLineNumbers={false}
mutation {
  company {
    deleteAddress(input: { entityId: 103 }) {
      entityId
      errors {
        message
        code
      }
    }
  }
}
```

#### Response

**`Example mutation response`**

```json title="Example mutation response" showLineNumbers={false}
{
  "data": {
    "company": {
      "deleteAddress": {
        "entityId": 103,
        "errors": []
      }
    }
  }
}
```

## Handle errors

Reads and writes report failures differently.

**Mutations** return errors in the payload. `entityId` is `null` and `errors` lists one or more entries with a `message` and a `code`. The HTTP status is still `200`.

| Code                | Meaning                                                                                                                              |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `VALIDATION_ERROR`  | A value is invalid, for example an unknown country or state, or a default flag set on an address that isn't enabled for that purpose |
| `PERMISSION_DENIED` | The caller's role, or the store's address book settings, doesn't allow the change                                                    |
| `UNAUTHORIZED`      | The request isn't authenticated for this operation                                                                                   |
| `NOT_FOUND`         | The address doesn't exist in the active Company                                                                                      |
| `NO_COMPANY`        | The signed-in customer has no active Company, such as a Super Admin who isn't masquerading                                           |
| `NOT_AUTHENTICATED` | The request has no signed-in customer                                                                                                |
| `FEATURE_DISABLED`  | The address book isn't enabled for the store                                                                                         |
| `B2B_DISABLED`      | B2B Edition isn't enabled for the store                                                                                              |
| `INTERNAL_ERROR`    | The change failed for another reason. Try again later                                                                                |

**Queries** return a standard GraphQL `errors` entry with the code in `extensions.code`.

| Code                | Meaning                                                            |
| ------------------- | ------------------------------------------------------------------ |
| `PERMISSION_DENIED` | The caller doesn't have permission to view the Company's addresses |
| `THROTTLED`         | The request was rate limited. Retry later                          |
| `UPSTREAM_ERROR`    | The address data couldn't be retrieved. Retry later                |

#### Request

**`Example mutation: Add an address without the required permission`**

```graphql title="Example mutation: Add an address without the required permission" showLineNumbers={false}
mutation {
  company {
    addAddress(
      input: {
        firstName: "Irène"
        lastName: "Joliot-Curie"
        address1: "500 W 2nd St"
        city: "Austin"
        countryCode: "US"
      }
    ) {
      entityId
      errors {
        message
        code
      }
    }
  }
}
```

#### Response

**`Example mutation response`**

```json title="Example mutation response" showLineNumbers={false}
{
  "data": {
    "company": {
      "addAddress": {
        "entityId": null,
        "errors": [
          {
            "message": "You do not have permission to create this address",
            "code": "PERMISSION_DENIED"
          }
        ]
      }
    }
  }
}
```

## Company hierarchy

The address operations don't take a Company ID, so they always act on the caller's active Company. They don't reach into a parent or child Company in an account hierarchy. To work with another Company's addresses, a Super Admin masquerades as that Company, which changes the active Company for the session.