Project readiness
A single GET returns whether a project can operate, how far its setup has come and which steps are still open — recomputed on every call, with stable step keys and no cached completion flag.
Before a project can send anything it has to hold a few things: contacts, a sender, a legal profile, and available balance. The readiness endpoints answer that in one call, so an onboarding screen, an installer or a pre-flight check does not have to ask five different endpoints and infer the answer.
There are two scopes. This page covers the project (home) report — the setup of the project as a whole. The channel report narrows the same shape to SMS or RCS and adds the countries that channel can reach.
The project-level setup report: what is done, what is left, and whether the project can operate.
Any Product API token with access to the project can read it; no extra scope is required.
It is a live report, not a saved state
There is no "onboarding finished" flag behind this. Every call recomputes the report from the project's current data, which has one consequence worth designing for: a completed step re-opens when its condition regresses. A project that spends its balance sees funds go back to completed: false, and operational follows it. Treat the response as the current answer, never as a milestone you can persist and stop asking about.
The report carries computedAt for exactly that reason. It is always "now" at the moment of the response — but a client that fetches once and renders for the next ten minutes holds an answer that is ten minutes old, and needs to know it.
The report
curl "https://api.instasent.com/v1/project/$INSTASENT_PROJECT/readiness" \
-H "Authorization: Bearer $INSTASENT_TOKEN"{
"entity": {
"scope": "project",
"projectType": "standard",
"channel": null,
"percent": 29,
"operational": false,
"steps": [
{ "key": "import", "completed": true, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true },
{ "key": "channels", "completed": true, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true },
{ "key": "legal-profile", "completed": false, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true },
{ "key": "funds", "completed": false, "isBlocker": true, "disabled": false, "skipped": false, "skippable": true },
{ "key": "campaign", "completed": false, "isBlocker": false, "disabled": false, "skipped": false, "skippable": true },
{ "key": "automation", "completed": false, "isBlocker": false, "disabled": false, "skipped": false, "skippable": true },
{ "key": "brand", "completed": false, "isBlocker": false, "disabled": false, "skipped": false, "skippable": true }
],
"warnings": [],
"reach": null,
"computedAt": "2026-09-04T16:20:31Z"
}
}scopestringproject here, channel on the channel report. Both scopes return the same object, so a client can render either with one renderer.
projectTypestringstandard or api_sms. It decides which steps the project gets — see the step catalogue.
channelstring | nullAlways null on this scope.
percentintegerCompleted steps over total steps, 0–100. 100 when there are no steps at all.
operationalbooleantrue when no blocker step is left incomplete — the project can work. Non-blocker steps do not hold it back, so a project can be operational at 60%.
stepsobject[]The setup milestones, in display order. Order is significant: render them as they arrive.
warningsobject[]Always [] on this scope. The one warning that exists — recommended-registrations — is per-channel and lives on the channel report. What has actually broken is a different endpoint: attention.
reachobject | nullAlways null on this scope. Send reach is a property of a channel, not of a project.
computedAtstringWhen the backend built this report, UTC ISO-8601.
A step
keystringStable identifier, kebab-case. The API ships no copy: the key is the contract and the label is yours to write and translate.
completedbooleanWhether the condition behind the step holds right now.
isBlockerbooleanWhen true, the project cannot operate until the step is completed. The incomplete blockers are exactly what operational: false is telling you about.
disabledbooleanWhen true, a prerequisite is missing and the user cannot act on the step yet — a campaign cannot be created before there are contacts. Render it, but don't offer the action.
skippedbooleanWhether the user hid this step from the dashboard checklist. A preference, not a fact: the backend reports it and honours none of it, so completed, isBlocker, disabled, percent and operational are all computed as if it were false. Each client decides what to do with it — hide the row, grey it out, or ignore the field entirely.
Unlike a dismissed attention item, a skip is stored on the platform and shared by everyone working on the project.
skippablebooleanWhether a user may hide this step at all. true for every step today; it exists so that the day one becomes mandatory, the decision is declared once here and every surface obeys it.
It outranks a stored skip: on a step that is not skippable, skipped is always false even if a preference was saved before it stopped being skippable. Nothing ever claims "you hid this" about something that can no longer be hidden, so there is no stale preference for you to clean up.
The step catalogue
The step set depends on projectType, and funds only appears where the account needs available balance. Both lists are ordered as returned.
standard — a project operated from the dashboard:
| Key | Blocker | Completed when |
|---|---|---|
import | yes | The project has at least one data source. |
channels | only while there is no sender | There is an active sender and no channel the customer asked for at signup is still without one. |
legal-profile | yes | The organization has filled in the legal profile that sender registrations are built from. |
funds | yes | The available balance is above the minimum. |
campaign | no | At least one campaign exists. Disabled until import is done. |
automation | no | At least one automation exists. Disabled until import is done. |
brand | no | The project has a brand with its basic details filled in. |
api_sms — a project used as a sending API rather than from the panel:
| Key | Blocker | Completed when |
|---|---|---|
token | yes | The organization has an API SMS token. |
channels | only while there is no sender | Same condition as above. |
funds | yes | The available balance is above the minimum. |
send-message | no | A message went out on SMS or RCS in the last 30 days. Disabled while there is no sender. |
Two of these deserve a note:
channelsis stricter to complete than to unblock. One active sender means the project can send, so the step stops being a blocker there. But if the customer asked for RCS at signup and only SMS is up, the step stays incomplete — the percentage reflects the work left without declaring a working project inoperative. ReadisBlockerfrom the response rather than assuming it.send-messageis a rolling window on purpose. It re-opens after 30 days without traffic, so the checklist doubles as a drift cue on an integration that has gone quiet.
Using it
Two integrations cover most cases:
Drive a setup checklist
Render
stepsin order, map eachkeyto your own copy, and usepercentfor the progress bar. Poll it after the user acts — the report is the only thing that decides a step is done.Pre-flight a send
Read
operationalbefore you start pushing traffic. When it isfalse, the incompleteisBlockersteps are the reason, and they name what to fix.
Readiness answers whether the project can send. Whether there is anyone to send to is a different question — count your audience for that.