How to

Check out as company user

Complete the checkout process as a company user, including quotation cart checkout, using Geins Merchant API

Prerequisites

  • Merchant API key (X-ApiKey)
  • Authenticated user session (JWT bearer token)
  • User associated with a company account
  • Existing cart with items (or a quotation cart ID)
Company addresses used during checkout are managed separately. See Manage company addresses to add, update, or remove addresses before checking out.

Goals

  • Check out a regular cart as a company user with company-managed addresses
  • Load a quotation cart in checkout and understand its restrictions
  • Place an order for a company cart or finalize a quotation order

Architecture at a glance

  • Authenticate company user → createOrUpdateCheckout (with company address selection) → placeOrder
  • Authenticate company user → createOrUpdateCheckout (with quotation cart) → finalizeQuotation

APIs used

  • Merchant API: https://merchantapi.geins.io/graphql

Step-by-step

Create or update checkout as a company user

When an authenticated company user calls createOrUpdateCheckout, the API automatically detects the company membership and applies company rules:

  • Addresses are restricted to those defined on the company. Custom billingAddress and shippingAddress fields are not accepted — use billingAddressId and shippingAddressId instead.
  • The response includes billingAddresses and shippingAddresses lists containing the eligible company addresses.
  • If no address ID is provided, the first matching address is selected by default.
Try it out in the GraphQL Playground using the query, headers and variables below.

Request example

mutation createOrUpdateCheckout(
  $cartId: String!
  $checkout: CheckoutInputType
  $channelId: String
  $languageId: String
  $marketId: String
) {
  createOrUpdateCheckout(
    cartId: $cartId
    checkout: $checkout
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  ) {
    billingAddress {
      addressId
      company
      addressLine1
      zip
      city
      country
    }
    shippingAddress {
      addressId
      company
      addressLine1
      zip
      city
      country
    }
    billingAddresses {
      addressId
      company
      addressLine1
      zip
      city
      country
    }
    shippingAddresses {
      addressId
      company
      addressLine1
      zip
      city
      country
    }
    shippingOptions {
      id
      displayName
      feeIncVat
      isSelected
    }
    paymentOptions {
      id
      displayName
      feeIncVat
      isSelected
    }
    checkoutStatus
  }
}
The shippingAddressId and billingAddressId fields are optional. If omitted, the first eligible address from the company is selected automatically.

Response example

200 OK
response.json
{
  "data": {
    "createOrUpdateCheckout": {
      "billingAddress": {
        "addressId": "101",
        "company": "Acme Trading AB",
        "addressLine1": "Industrivägen 10",
        "zip": "11122",
        "city": "Stockholm",
        "country": "SE"
      },
      "shippingAddress": {
        "addressId": "102",
        "company": "Acme Trading AB",
        "addressLine1": "Lagervägen 5",
        "zip": "11133",
        "city": "Stockholm",
        "country": "SE"
      },
      "billingAddresses": [
        {
          "addressId": "101",
          "company": "Acme Trading AB",
          "addressLine1": "Industrivägen 10",
          "zip": "11122",
          "city": "Stockholm",
          "country": "SE"
        }
      ],
      "shippingAddresses": [
        {
          "addressId": "102",
          "company": "Acme Trading AB",
          "addressLine1": "Lagervägen 5",
          "zip": "11133",
          "city": "Stockholm",
          "country": "SE"
        }
      ],
      "shippingOptions": [
        { "id": 1, "displayName": "Standard Shipping", "feeIncVat": 49.00, "isSelected": true }
      ],
      "paymentOptions": [
        { "id": 2, "displayName": "Invoice", "feeIncVat": 0.00, "isSelected": true }
      ],
      "checkoutStatus": "OK"
    }
  }
}

Place an order for a company cart

After setting up the checkout, place the order using placeOrder. The same company rules apply — use billingAddressId and shippingAddressId instead of free-form addresses.

Try it out in the GraphQL Playground using the query, headers and variables below.

Request example

mutation placeOrder(
  $cartId: String!
  $checkout: CheckoutInputType!
  $channelId: String
  $languageId: String
  $marketId: String
) {
  placeOrder(
    cartId: $cartId
    checkout: $checkout
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  ) {
    orderId
    publicId
  }
}

Response example

200 OK
response.json
{
  "data": {
    "placeOrder": {
      "orderId": "order-id-12345",
      "publicId": "ORD-2025-001234"
    }
  }
}

Check out a quotation cart

Quotation carts can also be loaded in createOrUpdateCheckout. This lets you preview shipping and payment options before finalizing. The following restrictions apply:

  • Only shipping and payment methods defined in the quotation are available.
  • The authenticated user must be the assigned buyer for the quotation.
  • The cart cannot be modified (items are locked).
  • Orders from quotation carts are placed via finalizeQuotation, not placeOrder.

Quotation status requirements

A quotation cart can only be used in getCart and createOrUpdateCheckout if it has an eligible status. The API rejects carts with terminal or inactive statuses:

StatusAllowed in checkoutNotes
PENDINGOnly if requireConfirmation is falseIf requireConfirmation is true, the buyer must first accept and the seller must confirm
ACCEPTEDOnly if requireConfirmation is falseIf requireConfirmation is true, the cart is blocked until the seller confirms
CONFIRMED✅ YesReady for checkout and finalization
DRAFT❌ NoNot yet sent to buyer
EXPIRED❌ NoValidity period has passed
CANCELED❌ NoCanceled by seller
REJECTED❌ NoRejected by buyer
FINALIZED❌ NoAlready converted to an order

When a quotation has requireConfirmation: true and is in PENDING status, the API returns an error indicating the quotation must be accepted by the buyer and confirmed by the seller before it can proceed. Similarly, an ACCEPTED quotation with requireConfirmation: true is blocked until the seller confirms it.

Pass the quotation cart ID as the cartId argument:

Try it out in the GraphQL Playground using the query, headers and variables below.

Request example

mutation createOrUpdateCheckout(
  $cartId: String!
  $checkout: CheckoutInputType
  $channelId: String
  $languageId: String
  $marketId: String
) {
  createOrUpdateCheckout(
    cartId: $cartId
    checkout: $checkout
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  ) {
    shippingOptions {
      id
      displayName
      feeIncVat
      isSelected
    }
    paymentOptions {
      id
      displayName
      feeIncVat
      isSelected
    }
    billingAddress {
      addressId
      company
      addressLine1
      city
      country
    }
    shippingAddress {
      addressId
      company
      addressLine1
      city
      country
    }
    checkoutStatus
  }
}
After previewing checkout options, finalize the quotation using the finalizeQuotation mutation — see Finalize a quotation order.

Response example

200 OK
response.json
{
  "data": {
    "createOrUpdateCheckout": {
      "shippingOptions": [
        { "id": 1, "displayName": "Freight", "feeIncVat": 0.00, "isSelected": true }
      ],
      "paymentOptions": [
        { "id": 2, "displayName": "Invoice 30 days", "feeIncVat": 0.00, "isSelected": true }
      ],
      "billingAddress": {
        "addressId": "101",
        "company": "Acme Trading AB",
        "addressLine1": "Industrivägen 10",
        "city": "Stockholm",
        "country": "SE"
      },
      "shippingAddress": {
        "addressId": "102",
        "company": "Acme Trading AB",
        "addressLine1": "Lagervägen 5",
        "city": "Stockholm",
        "country": "SE"
      },
      "checkoutStatus": "OK"
    }
  }
}

Options

Multi-market support

All mutations support optional parameters for multi-market functionality:

  • channelId: Target specific sales channels
  • languageId: Set content language
  • marketId: Target specific markets
Read more about channelId, languageId, and marketId in the how-to about using multi-market support.

Authenticated access

Authentication is required for company checkout. The API uses the JWT bearer token to identify the user's company membership and apply company-specific rules (addresses, pricing). Without a valid token, the checkout will not apply company logic.

For quotation carts, authentication is also required — the API verifies that the logged-in user matches the assigned buyer on the quotation.

Include the token in the Authorization header:

Authorization: Bearer {JWT_TOKEN}
See the authentication flow guide for details on obtaining and refreshing JWT tokens.

Common pitfalls

  • Passing billingAddress or shippingAddress directly — Company checkouts reject free-form address input. Use billingAddressId and shippingAddressId to select from predefined company addresses.
  • Calling placeOrder on a quotation cart — Quotation carts must be finalized via finalizeQuotation. The placeOrder mutation will return an error for quotation carts.
  • Quotation in wrong status — A quotation cart with status DRAFT, EXPIRED, CANCELED, REJECTED, or FINALIZED cannot be loaded in getCart or createOrUpdateCheckout. If requireConfirmation is enabled, the quotation must reach CONFIRMED status before checkout is possible.
  • Missing authentication — Both company checkout and quotation checkout require a valid JWT token. An unauthenticated request will fail.
  • Wrong buyer for quotation — A quotation cart can only be checked out by the user assigned as buyer on that quotation. Attempting to load another user's quotation returns an error.
Related