Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.firmhouse.com/llms.txt

Use this file to discover all available pages before exploring further.

Use this guide when you are building a React, Next.js, or other JavaScript storefront that needs to create subscriptions in Firmhouse. The flow is:
  1. Fetch products and plans from Firmhouse.
  2. Create a cart.
  3. Add products to the cart.
  4. Let customers update product quantities or remove products.
  5. Update address and customer details.
  6. Create a payment link and redirect the customer to payment.

Requirements

Before you start:
  • Create the products you want to sell in Firmhouse.
  • For Product-as-a-Service projects, create at least one plan.
  • Set up a payment provider.
  • Generate a project access token in Settings > Integrations. For client-side storefront calls, use a token with Storefront access.

Fetch products

Use the products query to show products configured in the Firmhouse Portal.
const projectAccessToken = "<Your-project-access-token>";

const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
  },
  body: JSON.stringify({
    query: `
      query Products($after: String, $first: Int) {
        products(after: $after, first: $first) {
          totalCount
          pageInfo {
            endCursor
            hasNextPage
          }
          nodes {
            id
            title
            slug
            available
            imageUrl
            priceCents
            priceWithSymbol
            productType
            sku
          }
        }
      }
    `,
    variables: {
      after: null,
      first: 10,
    },
  }),
});

const body = await response.json();
const { nodes: products, pageInfo } = body.data.products;
Use after and first for pagination. Pass pageInfo.endCursor as after to fetch the next page.

Fetch plans

For Product-as-a-Service projects, use the plans query to show the plans configured in Firmhouse.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
  },
  body: JSON.stringify({
    query: `
      query Plans($after: String, $first: Int) {
        plans(after: $after, first: $first) {
          totalCount
          pageInfo {
            endCursor
            hasNextPage
          }
          nodes {
            id
            name
            slug
            available
            currency
            monthlyAmountCents
            initialAmountIncludingTaxCents
            planProducts {
              quantity
              product {
                id
              }
            }
          }
        }
      }
    `,
    variables: {
      after: null,
      first: 10,
    },
  }),
});

const body = await response.json();
const { nodes: plans } = body.data.plans;

Create a cart

Create a cart with createCart. Firmhouse returns a subscription token for the draft subscription.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
  },
  body: JSON.stringify({
    query: `
      mutation CreateCart {
        createCart(input: {}) {
          subscription {
            token
          }
        }
      }
    `,
  }),
});

const body = await response.json();
const subscriptionToken = body.data.createCart.subscription.token;
Pass this token in the X-Subscription-Token header for later cart calls. In a web app, store it in a cookie or local storage so returning customers can continue the same checkout. If the default plan includes products, those products are automatically added when you create the cart.

Add products to the cart

Use createOrderedProduct to add a product.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      mutation CreateOrderedProduct($input: CreateOrderedProductInput!) {
        createOrderedProduct(input: $input) {
          orderedProduct {
            id
            title
            quantity
            priceIncludingTaxCents
            totalAmountIncludingTaxCents
          }
          subscription {
            monthlyAmountCents
            currency
            orderedProducts {
              id
              title
              quantity
              totalAmountIncludingTaxCents
            }
          }
          errors {
            attribute
            message
            path
          }
        }
      }
    `,
    variables: {
      input: {
        productId: products[0].id,
        quantity: 1,
      },
    },
  }),
});

const body = await response.json();
const { orderedProduct, subscription, errors } = body.data.createOrderedProduct;
Use orderedProduct to show an added-to-cart confirmation. Use subscription to update the cart UI with totals and cart contents. If you need to add metadata to ordered products, use a project access token with Write access and pass metadata. If multiple rows of the same product need different metadata, pass ensureNewRecord: true.

Remove products from the cart

Use destroyOrderedProduct to remove a product from the cart.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      mutation DestroyOrderedProduct($input: DestroyOrderedProductInput!) {
        destroyOrderedProduct(input: $input) {
          orderedProduct {
            id
          }
          subscription {
            monthlyAmountCents
            orderedProducts {
              id
              title
              quantity
            }
          }
        }
      }
    `,
    variables: {
      input: {
        id: orderedProduct.id,
      },
    },
  }),
});

const { data } = await response.json();
const { subscription } = data.destroyOrderedProduct;

Update product quantities

Use updateOrderedProductQuantity to update quantities in the cart.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      mutation UpdateOrderedProductQuantity($input: UpdateOrderedProductQuantityInput!) {
        updateOrderedProductQuantity(input: $input) {
          orderedProduct {
            id
            quantity
          }
          subscription {
            monthlyAmountCents
            orderedProducts {
              id
              title
              quantity
            }
          }
          errors {
            attribute
            message
            path
          }
        }
      }
    `,
    variables: {
      input: {
        orderedProduct: {
          id: orderedProduct.id,
          quantity: 2,
        },
      },
    },
  }),
});

const body = await response.json();
const { subscription, errors } = body.data.updateOrderedProductQuantity;

Update the subscription plan

Use updatePlan to change the active plan for the draft subscription.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      mutation UpdatePlan($planSlug: String!) {
        updatePlan(input: { planSlug: $planSlug }) {
          subscription {
            activePlan {
              id
              name
              slug
            }
            subscribedPlan {
              id
              name
              nextBillingDate
            }
          }
        }
      }
    `,
    variables: {
      planSlug: plans[0].slug,
    },
  }),
});

const body = await response.json();
const { subscription } = body.data.updatePlan;

Fetch current cart information

Use getSubscription to fetch the current cart state. This is useful when customers return to the storefront or when they navigate to checkout.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      query GetSubscription($token: ID!) {
        getSubscription(token: $token) {
          amountForStartingSubscriptionCents
          monthlyAmountCents
          checkoutUrl
          currency
          name
          lastName
          email
          orderedProducts {
            id
            title
            quantity
            totalAmountIncludingTaxCents
          }
        }
      }
    `,
    variables: {
      token: subscriptionToken,
    },
  }),
});

const body = await response.json();
const subscription = body.data.getSubscription;
If you want to use the default Firmhouse checkout page, redirect customers to subscription.checkoutUrl.

Update address details

Use updateAddressDetails to collect billing, invoice, and shipping details.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      mutation UpdateAddressDetails($input: UpdateAddressDetailsInput!) {
        updateAddressDetails(input: $input) {
          subscription {
            id
            name
            lastName
            email
            address
            city
            country
          }
          errors {
            attribute
            message
            path
          }
        }
      }
    `,
    variables: {
      input: {
        name: "John",
        lastName: "Doe",
        email: "johndoe@example.com",
        address: "Example street 1",
        city: "Rotterdam",
        country: "NL",
        termsAccepted: true,
      },
    },
  }),
});

const { data = {}, errors: requestErrors } = await response.json();
const { subscription, errors } = data.updateAddressDetails ?? {};
By default, Firmhouse requires name, email, address, city, and country before payment. You can change the required fields in Checkout > Preferences > Customer fields in your Firmhouse project. Use createSubscriptionFromCart to create a paymentUrl. Redirect the customer to that URL to complete payment.
const response = await fetch("https://portal.firmhouse.com/graphql", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Project-Access-Token": projectAccessToken,
    "X-Subscription-Token": subscriptionToken,
  },
  body: JSON.stringify({
    query: `
      mutation CreateSubscriptionFromCart($input: CreateSubscriptionFromCartInput!) {
        createSubscriptionFromCart(input: $input) {
          paymentUrl
          returnUrl
          errors {
            attribute
            message
            path
          }
        }
      }
    `,
    variables: {
      input: {
        returnUrl: "https://yourdomain.com/return",
        paymentPageUrl: "https://yourdomain.com/checkout",
      },
    },
  }),
});

const body = await response.json();
const { paymentUrl, returnUrl, errors } = body.data.createSubscriptionFromCart;
If required fields are missing, paymentUrl is null and errors explains what still needs to be collected.
If you have not added a payment provider to your Firmhouse project, creating a payment link will fail.