Every path sits under
/v1/partner/companies/{companyId}/compliance.
doola charges nothing through this API.
priceInCents is the state fee doola settles with you outside it. Submitting and confirming are two separate calls so you can collect from your own customer on your own timing, in between.The flow
While a report is still
FILLED, calling POST …/annual-reports again replaces it whole. Resend every answer and every companyDetails correction: anything you leave out is dropped, not carried over. See Submitting twice.The compliance calendar tells you when a report is due. The endpoints on this page tell you what to collect, what it costs, and where a report stands.
Requirements: what to collect
This endpoint is read-only. It describes what to collect; it neither starts a filing nor stores anything.
A Wyoming LLC, abbreviated. The full schema is in the Annual Report Filing section of the API reference.
Field groups
Every field carries agroup. Read the group rather than guessing from the fieldId. Questions come first, then company details.
Rendering a field
Treat the list as data. New
type values and new fields are added over time, so a client that switches on a closed set of ids will break; one that renders whatever it is handed will not.
Do not derive the requirements from the compliance calendar, or the other way round. They answer different questions from different sources, and for a small number of states they do not line up: a tracked due date does not promise a field list here, and the reverse holds too.
State fees: what it costs
This endpoint is read-only. It reports a price; it neither starts a filing, stores anything, nor charges anyone.
Read both flags before acting on
priceInCents:
isFilingRequired: is there a report to file at all? A zero on its own is ambiguous.isMinimum: is this the exact fee, or the state’s floor?
priceInCents. Where doola already holds every figure the state’s formula needs, the two are the same number.
A floor is not a quote
Some reports cannot be priced exactly before the figures exist: Wyoming’s, from its asset worksheet, and a Delaware C-corp’s when doola holds no authorized share count. Rather than refusing to answer, the endpoint reports the state’s floor and setsisMinimum: true:
authorizedShares in companyDetails, and the report is priced from them.
A Delaware C-corp whose share count doola already holds is priced exactly, so isMinimum is false even though the fee is computed from a formula. Branch on isMinimum, never on the state code: a state joining this list is not a breaking change.
Which answers move the fee
affectsFee: true on a requirements field marks a figure the state computes its fee from: the Wyoming asset answers, and the authorized share count on a Delaware C-corp. It is false on every field for almost every company, because most states price the report from data doola already holds.
affectsFee and isMinimum answer different questions. affectsFee says the state’s formula reads that field. isMinimum says doola does not hold the figure yet, so the quote is only the floor. A Delaware C-corp with its share count on file has affectsFee: true on the share count and isMinimum: false on the quote.
Use affectsFee to tell the customer which answers change what they pay. Do not use it to decide whether to collect a field; that is required.
When no report is filed at all
Whether a report is filed depends on the state and the entity type together. Alabama and Ohio require none from either, while in New Mexico and South Carolina only theLLC is exempt: a CCorp in either files and pays a fixed fee.
Both reads answer 200 and publish the same isFilingRequired: false, so they never disagree. The requirements come back with an empty fields list:
priceInCents: 0. Some states file an annual report and charge nothing for it; others file no annual report at all. Both report 0, and only isFilingRequired tells them apart.
Submitting the report
This call records the report and fixes its price. It does not start the filing: that is confirming, a separate call on your own timing.
A key in
companyDetails means “use this instead of what doola has”. Leaving it out means “keep what doola has”; you cannot clear a value. An address replaces the one on file whole, so it takes the full address object (line1, line2, city, state, postalCode, country), and a partial one is rejected rather than merged.
The response is 201 with the report on file:
isReplaced, so one type covers submitting, confirming and reading.
What is accepted
Only what the requirements published for this company. The check runs against the same listGET …/requirements returns, so the two cannot disagree.
- An answer to a field the company was not offered is refused with
400, naming the field. It is never dropped silently. - A field behind a
dependsOngate is only demanded, and only accepted, while the gate is open. Answer Wyoming’sQ16withNoand none of its asset questions are asked for; send one anyway and it is refused. - Every
requiredfield that applies has to be present. For a Delaware C-corp that isphoneNumberandphysicalAddress, the two required details doola does not hold. - Values are checked against the field’s
type:NUMBERandAMOUNTare non-negative decimals with at most two places,SELECTandRADIOtake one of the publishedoptions,DATEisyyyy-MM-dd. authorizedSharesandissuedSharesare whole numbers.perValuedSharesis money per share and keeps up to eight decimal places, enough for a $0.0001 par value; a ninth is refused rather than rounded away.
Submitting twice
One report per company per fiscal year. While the report is stillFILLED, a second submission for the same year replaces it: the answers are replaced whole, the report keeps its annualReportId, its price is fixed again from the new figures, and the response is 200 with isReplaced: true. Company details reset too, so resend any correction you made the first time. That is how you correct a mistake, and how a Wyoming report moves from the floor to its exact amount.
Once the report has moved past FILLED (you confirmed it, or doola is already working on or has filed that year) it is not overwritten: a second submission answers 409 E_ANNUAL_REPORT_ALREADY_EXISTS.
A FILLED report has no deadline. It waits until you confirm it or replace it.
Confirming the report
This call hands the report to doola to file. Nothing is charged: confirming is you committing to the filing. Call it when your own customer has paid you, or whenever suits your flow.
200 with the same report shape the submission returns.
The report moves to IN_PROGRESS, doola starts the filing, and you receive company_annual_report_submitted. From then on the report can no longer be replaced.
- Confirm every report, including one priced at
0. A state that charges nothing still has a report to file, and nothing happens until you confirm. - Confirming twice is safe. A report already past
FILLEDis returned unchanged, and doola never files it twice. - The report must belong to the company in the path. Any other report id answers
404 E_NOT_FOUND.
Reading reports back
These two endpoints are read-only. They show what the submission put on file and where doola has taken it since.
Listing a company’s reports
null, never dropped.
An empty list means doola holds nothing to show for the company, not that the company does not exist. A company that does not exist, or is not yours, is a
404.
Reading one report
answers. Most states ask no questions, so an empty list is the common case.
Status
One vocabulary across every response on this page.
A report the customer filed themselves, directly with the state, reads as
FILING_COMPLETED: it is filed, and hiding it would let you think the year is unfiled and file it a second time.
Every report you submitted is listed under one of these statuses. The one report you will not see is one the customer opened in doola’s own app that nobody has acted on yet: it is absent from the list and not readable by id either, so the two reads never disagree. New statuses may be added, so treat one you do not recognise as informational.
There is no channel yet for supplying what
AWAITING_INFORMATION is waiting on. When one lands it will be a required action, but no annual report action exists today, so do not poll for one.Webhooks
You do not have to poll a report to completion. Three webhook events cover it:
The first two carry
doolaCompanyId only: list the company’s reports to see which year moved. document_annualreport_uploaded arrives independently of the other two, so do not assume an order.
Errors
404 E_NOT_FOUND is deliberately undiscriminating: a report that does not exist and one that belongs to another company get the same answer, so an id you may not read is never confirmed to exist. Likewise a company that exists but is not yours is 404, never 403.
422 should not occur for a company created through this API: every US state and DC is covered for both LLC and CCorp. A company whose state files no annual report is not a 422 on the two reads. They answer 200 with isFilingRequired: false.