Skip to navigation

GraphQL Storefront API: Company address book

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 or the Create a customer impersonation token REST endpoint. On a Stencil storefront, you can also access a token directly from the page context.

Every operation on this page requires 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 and Dynamic tokens.

Where the operations live

Reads and writes sit in different places in the schema:

OperationLocation
List addressescustomer.activeCompany.addresses
Get one addresscustomer.activeCompany.address
Add, update, and delete an addresscompany.addAddress, company.updateAddress, company.deleteAddress
Address settings and extra field definitionssite.settings.b2b.config.address and site.settings.b2b.extraFields.address

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.

OperationAdminSenior BuyerJunior BuyerCustom role
activeCompany.addressesYesYesYesRequires View under Addresses
activeCompany.addressYesYesYesRequires View under Addresses
company.addAddressYesNoNoRequires Create under Addresses
company.updateAddressYesNoNoRequires Edit under Addresses
company.deleteAddressYesNoNoRequires 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, to decide whether to show address management controls.

To learn more about Company roles, see Company Roles and Permissions and the “Company Roles” section of 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:

    KeyWhen isEnabled is true
    address_bookThe address book is turned on for the store
    address_adminCompany users with the required role permission can add, update, and delete addresses
    address_sales_repA masquerading Super Admin can add, update, and delete addresses
    address_labelThe 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 typeValue typeType-specific fieldsWrite with
B2BTextExtraFieldDefinitionSingle-line textmaxLength, defaultTextextraFields.texts
B2BMultilineTextExtraFieldDefinitionMultiline textrows, defaultTextextraFields.multilineTexts
B2BNumberExtraFieldDefinitionNumbermaxValue, defaultNumberextraFields.numbers
B2BDropdownExtraFieldDefinitionOne choice from a listoptions, defaultValueextraFields.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.

Example query: Get the address settings
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
}
}
}
}
}
}
}

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

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.

Example query: List the first ten addresses with their extra fields
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
}
}
}
}
}
}
}
}

Filter and sort addresses

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

FilterTypeReturns
searchQueryStringAddresses matching the text in their address fields
isShippingBooleanAddresses enabled for shipping
isBillingBooleanAddresses enabled for billing
isDefaultShippingBooleanThe default shipping address
isDefaultBillingBooleanThe default billing address
cityStringAddresses in a city
stateStringAddresses in a state or province
countryStringAddresses 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
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.

Example query: Get an address by ID
query {
customer {
activeCompany {
address(entityId: 44) {
entityId
label
address1
address2
city
stateOrProvinceCode
postalCode
countryCode
isShipping
isBilling
isDefaultShipping
isDefaultBilling
}
}
}
}

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.

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.

Example mutation: Add an address with extra fields
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
}
}
}
}

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.

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

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.

Example mutation: Make an address the default shipping address
mutation {
company {
updateAddress(
input: {
entityId: 103
isShipping: true
isDefaultShipping: true
}
) {
entityId
errors {
message
code
}
}
}
}

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.

Example mutation: Delete an address
mutation {
company {
deleteAddress(input: { entityId: 103 }) {
entityId
errors {
message
code
}
}
}
}

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.

CodeMeaning
VALIDATION_ERRORA 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_DENIEDThe caller’s role, or the store’s address book settings, doesn’t allow the change
UNAUTHORIZEDThe request isn’t authenticated for this operation
NOT_FOUNDThe address doesn’t exist in the active Company
NO_COMPANYThe signed-in customer has no active Company, such as a Super Admin who isn’t masquerading
NOT_AUTHENTICATEDThe request has no signed-in customer
FEATURE_DISABLEDThe address book isn’t enabled for the store
B2B_DISABLEDB2B Edition isn’t enabled for the store
INTERNAL_ERRORThe change failed for another reason. Try again later

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

CodeMeaning
PERMISSION_DENIEDThe caller doesn’t have permission to view the Company’s addresses
THROTTLEDThe request was rate limited. Retry later
UPSTREAM_ERRORThe address data couldn’t be retrieved. Retry later
Example mutation: Add an address without the required permission
mutation {
company {
addAddress(
input: {
firstName: "Irène"
lastName: "Joliot-Curie"
address1: "500 W 2nd St"
city: "Austin"
countryCode: "US"
}
) {
entityId
errors {
message
code
}
}
}
}

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.