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:
- Fetch products and plans from Firmhouse.
- Create a cart.
- Add products to the cart.
- Let customers update product quantities or remove products.
- Update address and customer details.
- 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;
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.
Generate a payment link
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.