Check out as company user
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)
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
billingAddressandshippingAddressfields are not accepted — usebillingAddressIdandshippingAddressIdinstead. - The response includes
billingAddressesandshippingAddresseslists containing the eligible company addresses. - If no address ID is provided, the first matching address is selected by default.
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
}
}
{
"Accept": "application/json",
"X-ApiKey": "{MERCHANT_API_KEY}",
"Authorization": "Bearer {JWT_TOKEN}"
}
{
"cartId": "{CART_ID}",
"checkout": {
"shippingAddressId": "{SHIPPING_ADDRESS_ID}",
"billingAddressId": "{BILLING_ADDRESS_ID}",
"shippingId": 1,
"paymentId": 2,
"email": "buyer@company.com"
},
"channelId": "{CHANNEL_ID}",
"languageId": "{LANGUAGE_ID}",
"marketId": "{MARKET_ID}"
}
curl -X POST https://merchantapi.geins.io/graphql \
-H "Accept: application/json" \
-H "X-ApiKey: {MERCHANT_API_KEY}" \
-H "Authorization: Bearer {JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"query":"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}}","variables":{"cartId":"{CART_ID}","checkout":{"shippingAddressId":"{SHIPPING_ADDRESS_ID}","billingAddressId":"{BILLING_ADDRESS_ID}","shippingId":1,"paymentId":2,"email":"buyer@company.com"},"channelId":"{CHANNEL_ID}","languageId":"{LANGUAGE_ID}","marketId":"{MARKET_ID}"}}'
shippingAddressId and billingAddressId fields are optional. If omitted, the first eligible address from the company is selected automatically.Response example
200 OK{
"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.
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
}
}
{
"Accept": "application/json",
"X-ApiKey": "{MERCHANT_API_KEY}",
"Authorization": "Bearer {JWT_TOKEN}"
}
{
"cartId": "{CART_ID}",
"checkout": {
"shippingAddressId": "{SHIPPING_ADDRESS_ID}",
"billingAddressId": "{BILLING_ADDRESS_ID}",
"shippingId": 1,
"paymentId": 2,
"email": "buyer@company.com"
},
"channelId": "{CHANNEL_ID}",
"languageId": "{LANGUAGE_ID}",
"marketId": "{MARKET_ID}"
}
curl -X POST https://merchantapi.geins.io/graphql \
-H "Accept: application/json" \
-H "X-ApiKey: {MERCHANT_API_KEY}" \
-H "Authorization: Bearer {JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"query":"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}}","variables":{"cartId":"{CART_ID}","checkout":{"shippingAddressId":"{SHIPPING_ADDRESS_ID}","billingAddressId":"{BILLING_ADDRESS_ID}","shippingId":1,"paymentId":2,"email":"buyer@company.com"},"channelId":"{CHANNEL_ID}","languageId":"{LANGUAGE_ID}","marketId":"{MARKET_ID}"}}'
Response example
200 OK{
"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, notplaceOrder.
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:
| Status | Allowed in checkout | Notes |
|---|---|---|
PENDING | Only if requireConfirmation is false | If requireConfirmation is true, the buyer must first accept and the seller must confirm |
ACCEPTED | Only if requireConfirmation is false | If requireConfirmation is true, the cart is blocked until the seller confirms |
CONFIRMED | ✅ Yes | Ready for checkout and finalization |
DRAFT | ❌ No | Not yet sent to buyer |
EXPIRED | ❌ No | Validity period has passed |
CANCELED | ❌ No | Canceled by seller |
REJECTED | ❌ No | Rejected by buyer |
FINALIZED | ❌ No | Already 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:
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
}
}
{
"Accept": "application/json",
"X-ApiKey": "{MERCHANT_API_KEY}",
"Authorization": "Bearer {JWT_TOKEN}"
}
{
"cartId": "{QUOTATION_CART_ID}",
"checkout": {
"shippingId": 1,
"paymentId": 2,
"shippingAddressId": "{SHIPPING_ADDRESS_ID}",
"billingAddressId": "{BILLING_ADDRESS_ID}"
},
"channelId": "{CHANNEL_ID}",
"languageId": "{LANGUAGE_ID}",
"marketId": "{MARKET_ID}"
}
curl -X POST https://merchantapi.geins.io/graphql \
-H "Accept: application/json" \
-H "X-ApiKey: {MERCHANT_API_KEY}" \
-H "Authorization: Bearer {JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"query":"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}}","variables":{"cartId":"{QUOTATION_CART_ID}","checkout":{"shippingId":1,"paymentId":2,"shippingAddressId":"{SHIPPING_ADDRESS_ID}","billingAddressId":"{BILLING_ADDRESS_ID}"},"channelId":"{CHANNEL_ID}","languageId":"{LANGUAGE_ID}","marketId":"{MARKET_ID}"}}'
finalizeQuotation mutation — see Finalize a quotation order.Response example
200 OK{
"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 channelslanguageId: Set content languagemarketId: Target specific markets
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}
Common pitfalls
- Passing
billingAddressorshippingAddressdirectly — Company checkouts reject free-form address input. UsebillingAddressIdandshippingAddressIdto select from predefined company addresses. - Calling
placeOrderon a quotation cart — Quotation carts must be finalized viafinalizeQuotation. TheplaceOrdermutation will return an error for quotation carts. - Quotation in wrong status — A quotation cart with status
DRAFT,EXPIRED,CANCELED,REJECTED, orFINALIZEDcannot be loaded ingetCartorcreateOrUpdateCheckout. IfrequireConfirmationis enabled, the quotation must reachCONFIRMEDstatus 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.