Order a State Filing

Place a Filing with the state and monitor its status.

Filings API is in beta

Please reach out to us to enable the Filings API in your account: Support.

Overview

When you order an official Filing, Stable submits it to the state or other agency for the Company and bills your account for it. This recipe explores how you can use the API to find the Filings available for a Company, collect the information each Filing needs, place the Filing, and monitor its status.

This recipe uses the following concepts:

  • A Filing is a document Stable submits to an agency, such as an amendment or a request for a Certificate of Good Standing. Each Filing has a status you can check after you place it.
  • A Filing Type is the kind of Filing, such as amendment. The available Filing Types depend on the state or jurisdiction where you are searching.
  • A Filing Speed is how fast Stable processes the Filing: standard, expedited, or rush. Each Filing Speed also has its own fees and its own set of fields, which are dynamic information required as input on each Filing.

At a high level, the steps you'll complete are:

  1. List the Filing Types available for the Company in a state.
  2. List the Filing Speeds and fees for the Filing Type.
  3. Retrieve the fields for the selected Filing Speed and build your form.
  4. Place the Filing.
  5. Check the Filing's status.

Before you start

  • You must have an API key. See Authentication.
  • You must have a Company set up with at least one Registered Agent Location. See Registered Agent Locations.
  • The state you file in must be set in the Company's jurisdictions.
  • Keep in mind that placing a Filing is an irreversible action that will bill your account for the Filing's fees. If you'd like to test, you can use the Developer Sandbox.

Step 1: List the Filing Types

Send the Company ID and a two-letter state code to the List filing types. The response lists the Filing Types you can place for the Company in that state.

Request:

const STABLE_API_URL = 'https://api.usestable.com/v1';

const listFilingTypes = async (companyId, jurisdiction) => {
  const response = await fetch(
    `${STABLE_API_URL}/companies/${companyId}/jurisdictions/${jurisdiction}/filing-types`,
    { headers: { 'x-api-key': process.env.STABLE_API_KEY } },
  );
  if (!response.ok) throw new Error(`Failed to list filing types: ${response.status}`);
  return response.json();
};
import os

import requests

STABLE_API_URL = "https://api.usestable.com/v1"


def list_filing_types(company_id, jurisdiction):
    response = requests.get(
        f"{STABLE_API_URL}/companies/{company_id}/jurisdictions/{jurisdiction}/filing-types",
        headers={"x-api-key": os.environ["STABLE_API_KEY"]},
    )
    response.raise_for_status()
    return response.json()

Here is a minimal subset of the response:

{
  "filingTypes": [
    { "filingType": "amendment", "name": "Amendment" },
    { "filingType": "certificate-of-good-standing", "name": "Certificate of Good Standing" },
    ...
  ]
}

Note the following:

  • Each filingType is generally stable for each jurisdiction. You can reuse these for subsequent calls.
  • An empty filingTypes array means no Filings are available for the Company in that state.
  • The API returns HTTP 404 NOT_IN_JURISDICTION if the state is not one of the Company's jurisdictions.
  • The API returns HTTP 409 REGISTERED_AGENT_PENDING while Stable is setting up the Company's registered agent service. Retry once the registered agent Location is active. See Create a Registered Agent Location for more guidance.

Step 2: List the Filing Speeds

Send the filingType from Step 1 to the List filing speeds API. The response lists the Filing Speeds you can choose and the fees for each.

const listFilingSpeeds = async (companyId, jurisdiction, filingType) => {
  const response = await fetch(
    `${STABLE_API_URL}/companies/${companyId}/jurisdictions/${jurisdiction}/filing-types/${filingType}/filing-speeds`,
    { headers: { 'x-api-key': process.env.STABLE_API_KEY } },
  );
  if (!response.ok) throw new Error(`Failed to list filing speeds: ${response.status}`);
  return response.json();
};
def list_filing_speeds(company_id, jurisdiction, filing_type):
    response = requests.get(
        f"{STABLE_API_URL}/companies/{company_id}/jurisdictions/{jurisdiction}/filing-types/{filing_type}/filing-speeds",
        headers={"x-api-key": os.environ["STABLE_API_KEY"]},
    )
    response.raise_for_status()
    return response.json()

Here is a minimal subset of the response:

{
  "filingSpeeds": [
    {
      "filingSpeed": "standard",
      "name": "Standard",
      "agencyName": "Division of Corporations",
      "description": "Certificate of Amendment",
      "filingFee": 7500,
      "agencyFee": 10900
    },
    ...
  ]
}

Note the following:

  • Every Filing Type offers standard. Whether expedited and rush are offered depends on the agency and the Filing Type.
  • Filing Speeds describe Stable's processing speed and may not match the names the agency uses for its service tiers.
  • Fees are in cents. filingFee is Stable's fee and agencyFee is the agency's fee, which depends on the tier. Placing the Filing charges both to your Stable account.

Step 3: Build your form

Send the filingSpeed from Step 2 to the Retrieve a filing type at a selected speed API. The response includes the Filing Speed's fees and the fields the Filing needs as input.

const getFilingSpeed = async (companyId, jurisdiction, filingType, filingSpeed) => {
  const response = await fetch(
    `${STABLE_API_URL}/companies/${companyId}/jurisdictions/${jurisdiction}/filing-types/${filingType}/filing-speeds/${filingSpeed}`,
    { headers: { 'x-api-key': process.env.STABLE_API_KEY } },
  );
  if (!response.ok) throw new Error(`Failed to retrieve filing speed: ${response.status}`);
  return response.json();
};
def get_filing_speed(company_id, jurisdiction, filing_type, filing_speed):
    response = requests.get(
        f"{STABLE_API_URL}/companies/{company_id}/jurisdictions/{jurisdiction}/filing-types/{filing_type}/filing-speeds/{filing_speed}",
        headers={"x-api-key": os.environ["STABLE_API_KEY"]},
    )
    response.raise_for_status()
    return response.json()

Here is a minimal subset of the response:

{
  "filingSpeed": "standard",
  "name": "Standard",
  "agencyName": "Division of Corporations",
  "description": "Certificate of Amendment",
  "filingFee": 7500,
  "agencyFee": 10900,
  "fields": [
    {
      "key": "authorized_shares",
      "label": "Authorized shares",
      "type": "number",
      "required": true,
      "format": null,
      "description": "The number of shares created when you formed your corporation.",
      "repeatable": false,
      "options": null,
      "pattern": null,
      "fields": null
    },
    ...
  ]
}

Build a form control for each field and collect the answers from a user. Use each field's properties as follows:

PropertyUse
keyThe name of the value in values.
labelThe label for the form control.
typeOne of string, number, boolean, or object. An object field has its own sub-fields in fields. Render them recursively.
requiredWhether this field is required, or whether a null value can be sent.
formatOne of email, phone, or date. A date must be YYYY-MM-DD.
descriptionHint text to show with the form control.
repeatableWhether the field accepts an array of objects instead of one object.
optionsThe values the field accepts. Send the option's value.
patternA regular expression the value must match.

Here is a valid values object for the fields above:

{
  "authorized_shares": 10000000
}

Note the following:

  • Fields change for each state, Filing Type, and Filing Speed. Build the form from the response each time instead of hard-coding it.
  • An empty fields array means the Filing needs no information beyond the Company. Send an empty values object.
  • If a value is missing, invalid, or not one of the fields, the API returns HTTP 400 VALIDATION_ERROR with a readable message when you place the Filing.

Step 4: Place the Filing

Send the Filing Type, Filing Speed, a contact email, and the values from Step 3 to the Place a filing API. The contact email is who Stable contacts if there is a problem with the information provided, or if the agency reports some problem while processing the Filing.

This request requires an Idempotency-Key header. Generate a key for each Filing, such as a UUID, and save it with your record before you send the request. Reuse the same key when you retry. See Idempotency to understand the behavior.

const placeFiling = async (companyId, jurisdiction, { filingType, filingSpeed, values, idempotencyKey }) => {
  const response = await fetch(
    `${STABLE_API_URL}/companies/${companyId}/jurisdictions/${jurisdiction}/filings`,
    {
      method: 'POST',
      headers: {
        'x-api-key': process.env.STABLE_API_KEY,
        'Content-Type': 'application/json',
        'Idempotency-Key': idempotencyKey,
      },
      body: JSON.stringify({
        filingType,
        filingSpeed,
        filingContact: { email: '[email protected]' },
        values,
      }),
    },
  );
  if (!response.ok) {
    const { code, message } = await response.json();
    throw new Error(`Failed to place filing: ${response.status} ${code} ${message}`);
  }
  return response.json();
};
def place_filing(company_id, jurisdiction, filing_type, filing_speed, values, idempotency_key):
    response = requests.post(
        f"{STABLE_API_URL}/companies/{company_id}/jurisdictions/{jurisdiction}/filings",
        json={
            "filingType": filing_type,
            "filingSpeed": filing_speed,
            "filingContact": {"email": "[email protected]"},
            "values": values,
        },
        headers={
            "x-api-key": os.environ["STABLE_API_KEY"],
            "Idempotency-Key": idempotency_key,
        },
    )
    if not response.ok:
        error = response.json()
        raise Exception(f"Failed to place filing: {response.status_code} {error.get('code')} {error['message']}")
    return response.json()

You'll receive this response:

{
  "id": "6c4d7272-7390-4b38-8c9f-fecedfc33d3c",
  "name": "Delaware Amendment Filing",
  "status": "inProgress",
  "jurisdiction": "DE",
  "createdAt": "2026-10-29T18:10:51.350Z"
}

Note the following:

  • Retrying with the same Idempotency-Key and the same body returns the original Filing. Stable does not place or charge for a second one.
  • Sending the same Idempotency-Key with a different body returns HTTP 409 IDEMPOTENCY_CONFLICT. Use a new key to place a different Filing.

Step 5: Check the Filing's status

Use the List all of a company's filings API and find the Filing by its id. To list the Filings in one state, use List a company's filings in one jurisdiction.

const getFiling = async (companyId, filingId) => {
  const response = await fetch(`${STABLE_API_URL}/companies/${companyId}/filings`, {
    headers: { 'x-api-key': process.env.STABLE_API_KEY },
  });
  if (!response.ok) throw new Error(`Failed to list filings: ${response.status}`);
  const { edges } = await response.json();
  return edges.map(({ node }) => node).find((filing) => filing.id === filingId);
};
def get_filing(company_id, filing_id):
    response = requests.get(
        f"{STABLE_API_URL}/companies/{company_id}/filings",
        headers={"x-api-key": os.environ["STABLE_API_KEY"]},
    )
    response.raise_for_status()
    filings = [edge["node"] for edge in response.json()["edges"]]
    return next((filing for filing in filings if filing["id"] == filing_id), None)

Here is a minimal subset of the response:

{
  "edges": [
    {
      "cursor": "MA==",
      "node": {
        "id": "6c4d7272-7390-4b38-8c9f-fecedfc33d3c",
        "name": "Delaware Amendment Filing",
        "status": "complete",
        "jurisdiction": "DE",
        "createdAt": "2026-10-29T18:10:51.350Z"
      }
    }
  ],
  ...
}

Note the following:

  • status can be one of:
    • inProgress: the Filing is being processed by Stable and the agency.
    • complete: the state completed the Filing.
    • cancelled: the Filing was cancelled and will not be completed.
  • See Pagination and Filtering to understand how to page through results.

Recap

You've placed a Filing for a Company and polled for its status to be updated.

Keep these things in mind as you complete your integration:

  • Build your form from the fields the API returns. They change for each state, Filing Type, and Filing Speed.
  • Save the Idempotency-Key with your record before you place the Filing, then reuse it on every retry to prevent double charges or submissions.
  • Choose a contact email you actively monitor.

See Companies and Create a Registered Agent Location to keep learning about the concepts explored in this recipe.



Did this page help you?