Create a Registered Agent Location
Learn how to add a new registered agent Location to your existing Organization so a company can receive service of process and state correspondence in a given jurisdiction.
This guide covers registered agent locations only. To create a CMRA location (a physical mailbox for general mail reception), see Create and Onboard a New CMRA Location instead.
Overview
A registered agent location is the address at which Stable, acting as registered agent, accepts service of process and official state mail on behalf of one of your companies in a single US jurisdiction. Unlike a CMRA, a registered agent location:
- Is tied to an existing
Company(you must create the company first) - Does not require USPS Form 1583, document uploads, or a signature packet
- Is requested with a 2-letter US state code (for example
WYorDE) rather than an IATA-style facility code - May briefly return with
status: "pending"while we finish provisioning the address with our partners
You'll learn how to:
- Create a
Company(if you don't have one yet) - Create a registered agent
Locationfor that company in a given jurisdiction - Provide jurisdiction-specific extra information (e.g. for Wyoming)
- Handle a
pendingresponse and detect when the location becomesactive
Prerequisites
Before you begin, you'll need:
- A Stable API key
- A registered agent jurisdiction code (a 2-letter US state or territory code, e.g.
WY,DE,CA). See Location codes for the full list - The
idof an existingCompany. If you don't have one yet, you'll create it as Step 1 below
Authentication
All API requests require authentication using your API key in the x-api-key header:
curl -H "x-api-key: your-api-key" https://api.usestable.com/v1/...const fetch = require('node-fetch');
const url = 'https://api.usestable.com/v1/locations';
const options = {
headers: {
'x-api-key': 'your-api-key',
'Content-Type': 'application/json',
},
// ...
};
fetch(url, options)
.then(response => console.log('Response:', response.json()))
.catch(error => console.error('Error:', error));import requests
url = 'https://api.usestable.com/v1/...'
headers = {
'x-api-key': 'your-api-key',
'Content-Type': 'application/json',
}
response = requests.get(url, headers=headers)
if response.ok:
print(response.json())
else:
print(f"Error: {response.status_code}, {response.text}")
API Key SecurityKeep your API key secure and never expose it in client-side code. All API requests should be made from your backend services.
Process Overview
The registered agent Location creation flow follows these steps:
- Create a
Company(or reuse an existing one) - Create the registered agent
Locationin a given jurisdiction - Handle the response, including the possible
pendingstatus
There is no document upload step and no signature packet. Most registered agent locations are returned in active status on the first request.
Step 1: Create a Company
A registered agent location must be attached to a Company. If you've already created the company, skip to Step 2 and use its id.
curl -X POST https://api.usestable.com/v1/companies \
-H "x-api-key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"legalName": "Ada'\''s Apples LLC",
"entityType": "limited-liability-company",
"jurisdictions": ["WY"],
"formationJurisdiction": "WY",
"formationDate": "2024-01-15"
}'const fetch = require('node-fetch');
const url = 'https://api.usestable.com/v1/companies';
const body = {
legalName: "Ada's Apples LLC",
entityType: 'limited-liability-company',
jurisdictions: ['WY'],
formationJurisdiction: 'WY',
formationDate: '2024-01-15',
};
const options = {
method: 'POST',
headers: {
'x-api-key': 'your-api-key',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
};
fetch(url, options)
.then(response => response.json())
.then(data => console.log('Response:', data))
.catch(error => console.error('Error:', error));import requests
url = 'https://api.usestable.com/v1/companies'
body = {
"legalName": "Ada's Apples LLC",
"entityType": "limited-liability-company",
"jurisdictions": ["WY"],
"formationJurisdiction": "WY",
"formationDate": "2024-01-15",
}
headers = {
"x-api-key": "your-api-key",
"Content-Type": "application/json",
}
response = requests.post(url, headers=headers, json=body)
if response.ok:
print("Response:", response.json())
else:
print(f"Error: {response.status_code}, {response.text}")Save the id returned in the response. You'll pass it as companyId in Step 2.
Step 2: Create the Registered Agent Location
Send a POST /v1/locations with the jurisdiction's 2-letter state code as locationCode and the company's id as companyId.
curl -X POST https://api.usestable.com/v1/locations \
-H "x-api-key: your-api-key" \
-H "Content-Type: application/json" \
-d '{
"locationCode": "DE",
"companyId": "550e8400-e29b-41d4-a716-446655440000"
}'const fetch = require('node-fetch');
const url = 'https://api.usestable.com/v1/locations';
const body = {
locationCode: 'DE',
companyId: '550e8400-e29b-41d4-a716-446655440000',
};
const options = {
method: 'POST',
headers: {
'x-api-key': 'your-api-key',
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
};
fetch(url, options)
.then(response => response.json())
.then(data => console.log('Response:', data))
.catch(error => console.error('Error:', error));import requests
url = 'https://api.usestable.com/v1/locations'
body = {
"locationCode": "DE",
"companyId": "550e8400-e29b-41d4-a716-446655440000",
}
headers = {
"x-api-key": "your-api-key",
"Content-Type": "application/json",
}
response = requests.post(url, headers=headers, json=body)
if response.ok:
print("Response:", response.json())
else:
print(f"Error: {response.status_code}, {response.text}")Field Reference
locationCode(required): 2-letter US state or territory code such asDE,WY,CA. See Location codes for the full list of registered agent jurisdictions. Do not pass an IATA-style CMRA code (e.g.SFO1) here, it will be routed through the CMRA flow.companyId(required): UUID of an existingCompany. The location will be created as that company's registered agent in the chosen jurisdiction.autoScan(optional): For registered agent locations, onlytrueis permitted. The field defaults totrue, so you can omit it.additionalInformation(required for some jurisdictions, e.g. Wyoming): Jurisdiction-specific extra data. See Jurisdiction-Specific Requirements below.metadata(optional): Arbitrary key/value pairs to attach to the location. Each value must be a string or number.
Fields that are not allowed
platformPrefillis for CMRA onboarding only and will be rejected for registered agent locations.
Response (active)
In most cases, the request returns with status: "active" once provisioning completes. The address fields will be populated with the registered agent address assigned to your company in that jurisdiction:
{
"id": "70a3d702-bf1d-4153-8eb2-d3d889aff7f0",
"status": "active",
"address": {
"line1": "<assigned registered agent street>",
"line2": "",
"city": "<city>",
"state": "DE",
"postalCode": "<postal code>"
},
"type": "registeredAgent",
"onboarding": {
"status": "complete"
},
"metadata": null
}Response (pending)
If provisioning takes longer than the request window allows, the response will return with status: "pending". The address is still populated in this case, with the same fields as the active response:
{
"id": "70a3d702-bf1d-4153-8eb2-d3d889aff7f0",
"status": "pending",
"address": {
"line1": "<assigned registered agent street>",
"line2": "",
"city": "<city>",
"state": "DE",
"postalCode": "<postal code>"
},
"type": "registeredAgent",
"onboarding": {
"status": "complete"
},
"metadata": null
}A pending response means the location has been accepted and is being provisioned with our partners. Once provisioning completes, the location will transition to active. Keep the id from the response and poll the GET endpoint to detect the transition.
Step 3: Handling a Pending Location
When a location is returned as pending, do not retry the create request. The first create call has already succeeded and provisioning is underway; a second create call for the same company and jurisdiction will be rejected with an error (see Registered agent already exists below). Instead, save the id from the create response and poll for status updates.
Polling for Activation
Call GET /v1/locations/{id} on an interval until status becomes active. We recommend exponential backoff starting at around 1 second and capping at around 30 seconds between polls.
curl https://api.usestable.com/v1/locations/70a3d702-bf1d-4153-8eb2-d3d889aff7f0 \
-H "x-api-key: your-api-key"async function waitForActive(locationId, apiKey, { maxAttempts = 20 } = {}) {
let delay = 1000;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const res = await fetch(
`https://api.usestable.com/v1/locations/${locationId}`,
{ headers: { 'x-api-key': apiKey } },
);
const location = await res.json();
if (location.status === 'active') {
return location;
}
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 2, 30000);
}
throw new Error('Location did not become active in time');
}import time
import requests
def wait_for_active(location_id, api_key, max_attempts=20):
delay = 1
for _ in range(max_attempts):
res = requests.get(
f"https://api.usestable.com/v1/locations/{location_id}",
headers={"x-api-key": api_key},
)
location = res.json()
if location["status"] == "active":
return location
time.sleep(delay)
delay = min(delay * 2, 30)
raise TimeoutError("Location did not become active in time")Response Once Active
{
"id": "70a3d702-bf1d-4153-8eb2-d3d889aff7f0",
"status": "active",
"address": {
"line1": "<assigned registered agent street>",
"line2": "",
"city": "<city>",
"state": "DE",
"postalCode": "<postal code>"
},
"type": "registeredAgent",
"onboarding": {
"status": "complete"
},
"metadata": null
}
Do not retry the create requestA
pendingstatus is not a failure. The create call succeeded and provisioning is underway. Retrying thePOST /v1/locationscall for the same company and jurisdiction will be rejected with a409 Conflict(Registered agent already exists). Always poll the existingidinstead.
Jurisdiction-Specific Requirements
Some jurisdictions require additional information on the company before a registered agent location can be activated. Pass these through additionalInformation on the create request.
Wyoming (WY)
WY)Wyoming requires additionalInformation to be provided on registered agent creation. Pass the company's management structure and the officials responsible for the entity:
{
"locationCode": "WY",
"companyId": "550e8400-e29b-41d4-a716-446655440000",
"additionalInformation": {
"managementType": "Member Managed",
"officialMember": {
"firstName": "Ada",
"lastName": "Lovelace"
}
}
}Supported fields on additionalInformation:
managementType(string, e.g."Member Managed"or"Manager Managed")official,officialDirector,officialManager,officialMember,officialPartner,officialPresident,officialSecretary,officialTreasurer: each takes{ firstName, lastName }
Provide the official fields that match the company's entity type and management structure.
Other Jurisdictions
Most jurisdictions do not require additionalInformation. Pass only locationCode and companyId. If we add new jurisdiction-specific requirements, this section will be updated.
Error Handling
Registered agent already exists
{
"status": 409,
"message": "A registered agent already exists for Ada's Apples LLC in DE."
}You'll get this when you call POST /v1/locations for a company and jurisdiction that already has a registered agent location, including the case where the first call returned status: "pending" and is still provisioning. Don't retry the create call; instead, look up the existing location by ID (or by listing the company's locations) and poll its status until it becomes active.
Missing companyId
companyId{
"status": 400,
"message": "companyId is required for registered agent locations"
}Solution: Include companyId in your request body.
platformPrefill Not Allowed
platformPrefill Not Allowed{
"status": 400,
"message": "platformPrefill is not allowed for registered agent locations"
}Solution: Remove platformPrefill from your request body. It is only used for CMRA locations.
autoScan: false Not Allowed
autoScan: false Not Allowed{
"status": 400,
"message": "autoScan cannot be false for registered agent locations"
}Solution: Omit autoScan or set it to true. Registered agent mail is always scanned.
Missing additionalInformation for Wyoming
additionalInformation for Wyoming{
"status": 400,
"message": "additionalInformation is required for WY"
}Solution: Include the required additionalInformation object on the create request. See Wyoming above.
Invalid Location Code
{
"status": 400,
"message": "Validation error(s) when parsing body: 'locationCode must be a CMRA code (...) or a registered agent jurisdiction code (AK, AL, ...). See https://docs.usestable.com/docs/location-codes for more information.'"
}Solution: Use one of the supported 2-letter jurisdiction codes from the Location codes page.
Best Practices
- Always store the
idfrom apendingresponse before polling. Treat the create call as a single-use action: a second create for the same company and jurisdiction returns409 Conflict. - Use exponential backoff when polling for
activestatus. Most pending locations activate in well under a minute. - Validate Wyoming inputs on your side before submitting, so you can surface clear errors to your users about which officials are needed.
- Surface a clear loading state to your end user while a registered agent location is
pending, with messaging that the address is being provisioned.
Updated 2 days ago
