How to

Finalize a quotation order

Accept, reject, or finalize seller-created quotations into orders using Geins Merchant API

Overview

Quotations follow a lifecycle that turns a seller-created proposal into an order. The buyer can accept or reject a quotation, and once accepted and confirmed, either the buyer or the seller can finalize it to place the order.

Quotation statuses

StatusDescription
DRAFTQuotation is being prepared by the seller and is not yet visible to the buyer
PENDINGQuotation has been sent to the buyer and is awaiting a response
ACCEPTEDBuyer has accepted the quotation; awaiting seller confirmation if required
CONFIRMEDSeller has confirmed the accepted quotation; ready to be finalized
FINALIZEDQuotation has been finalized and an order has been placed
REJECTEDBuyer has rejected the quotation
EXPIREDQuotation validity period (validTo) has passed without action
CANCELEDQuotation has been canceled by the seller
When settings.requireConfirmation is true, the quotation must reach CONFIRMED status before it can be finalized. The isBlockedFromCheckout flag on the cart will be true until the seller confirms. When requireConfirmation is false, the quotation moves directly from ACCEPTED to being ready for finalization.

Prerequisites

  • Merchant API key
  • JWT token for the authenticated buyer
  • Known quotation ID (from listQuotationCarts or getQuotationCart)

Goal

  • Accept or reject a quotation
  • Finalize a confirmed quotation into an order
  • Understand quotation statuses and the seller confirmation flow

Architecture at a glance

  • Buyer accepts quotation → Seller confirms (if required) → Buyer or seller finalizes → Order created
  • Buyer rejects quotation → Quotation closed

APIs used

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

Step-by-step

Check the quotation status

Before acting on a quotation, retrieve it to inspect the current status and settings.

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

Request example

query getQuotationCart(
  $quotationId: Guid!
  $channelId: String
  $languageId: String
  $marketId: String
) {
  getQuotationCart(
    quotationId: $quotationId
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  ) {
    id
    isBlockedFromCheckout
    quotation {
      quotationNumber
      status
      validTo
      settings {
        requireConfirmation
      }
      orderId
    }
  }
}

Response example

200 OK
response.json
{
  "data": {
    "getQuotationCart": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "isBlockedFromCheckout": true,
      "quotation": {
        "quotationNumber": "2603-01-0001-00",
        "status": "PENDING",
        "validTo": "2026-04-30T23:59:59Z",
        "settings": {
          "requireConfirmation": true
        },
        "orderId": null
      }
    }
  }
}

Accept a quotation

Call acceptQuotation to indicate the buyer agrees with the quotation terms. The status changes from PENDING to ACCEPTED.

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

Request example

mutation acceptQuotation(
  $quotationId: Guid!
  $channelId: String
  $languageId: String
  $marketId: String
) {
  acceptQuotation(
    quotationId: $quotationId
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  )
}
The channelId, languageId, and marketId arguments are optional and can be left out to use default values.

Response example

200 OK
response.json
{
  "data": {
    "acceptQuotation": true
  }
}

Reject a quotation

If the buyer does not agree with the terms, call rejectQuotation. The status changes to REJECTED and cannot be acted upon further.

Request example

mutation rejectQuotation(
  $quotationId: Guid!
  $channelId: String
  $languageId: String
  $marketId: String
) {
  rejectQuotation(
    quotationId: $quotationId
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  )
}

Response example

200 OK
response.json
{
  "data": {
    "rejectQuotation": true
  }
}

Finalize the quotation into an order

Once the quotation has been confirmed (status CONFIRMED) and isBlockedFromCheckout is false, call finalizeQuotation to place the order. Either the buyer or the seller can perform this step.

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

Request example

mutation finalizeQuotation(
  $quotationId: Guid!
  $channelId: String
  $languageId: String
  $marketId: String
) {
  finalizeQuotation(
    quotationId: $quotationId
    channelId: $channelId
    languageId: $languageId
    marketId: $marketId
  )
}

Response example

200 OK
response.json
{
  "data": {
    "finalizeQuotation": true
  }
}

After finalization, retrieve the quotation cart to get the orderId:

query getQuotationCart($quotationId: Guid!) {
  getQuotationCart(quotationId: $quotationId) {
    quotation {
      status
      orderId
    }
  }
}
200 OK
response.json
{
  "data": {
    "getQuotationCart": {
      "quotation": {
        "status": "FINALIZED",
        "orderId": "12345"
      }
    }
  }
}

Options

Multi-market support

All mutations support optional parameters for multi-market configurations:

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

Authenticated access

These mutations require JWT authentication. Include the JWT bearer token in the Authorization header:

"Authorization": "Bearer {JWT_TOKEN}"
Read more about obtaining and using JWT tokens in the guide about the authentication flow.

Common pitfalls

  • Calling finalizeQuotation when isBlockedFromCheckout is true will fail — the seller must confirm the quotation first when settings.requireConfirmation is true
  • Accepting or finalizing an expired quotation (past validTo) will fail
  • Once a quotation is REJECTED, FINALIZED, or CANCELED, no further status transitions are possible
  • Do not use placeOrder on a quotation cart — use finalizeQuotation instead
Related