Skip to content
Join our Slack

Publish the draft

POST
/v1/forms/{form_public_id}/publish
curl --request POST \
--url https://platform.fomr.io/v1/forms/example/publish \
--header 'Authorization: Bearer <token>'

Publishes the stored draft and answers the form resource. A draft identical to the latest published version republishes that version number; any other draft becomes the next version. There are no preconditions: the version number is computed under the form lock, so the last call wins. Publish Validation runs first, and a draft that is not ready is refused with 422 carrying the same Publish Issues validate returns; a draft flagged by automated safety checks is refused with 422 FORM_SAFETY_REJECTED, and a token owner without a verified email with 403 FORM_PUBLISH_EMAIL_UNVERIFIED. A Private Template cannot be published and is refused with 400 INVALID_INPUT at is_template. Needs forms:write and form_publish. Costs 1 API credit.

form_public_id
required
string
>= 10 characters <= 10 characters /^[A-Za-z0-9_-]+$/

The public ID of the form or Private Template.

Default Response

Media typeapplication/json
object
public_id
required

The public ID of the form. Use it in every other form call.

string
>= 10 characters <= 10 characters /^[A-Za-z0-9_-]+$/
name
required

Form name shown in the app.

string
is_template
required

Whether the resource is a Private Template rather than a form.

boolean
status
required

Lifecycle state: draft while nothing is published, published once a version is live, archived when the form is no longer editable.

string
Allowed values: draft published archived
has_unpublished_changes
required

Whether publishing would change what respondents see. True for a form that has never been published.

boolean
published_version
required
Any of:
integer
>= -9007199254740991 <= 9007199254740991
published_at
required
Any of:
string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
source_template_public_id
required
Any of:
string
>= 10 characters <= 10 characters /^[A-Za-z0-9_-]+$/
workspace_public_id
required

The workspace the form belongs to. Change it with the move operation.

string
>= 4 characters <= 4 characters
access
required

Whether the form takes submissions.

object
is_closed
required

Whether the form was closed by hand.

boolean
close_at
required
Any of:
string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
open_at
required
Any of:
string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
accepting_submissions
required

Whether the form takes a submission right now. Derived from is_closed, the schedule, and whether a version is published.

boolean
duplicate_response_rule
required
Any of:
One of:

One completed response per respondent IP address.

object
type
required

The matching method: ip_address, submission_id, or component.

string
Allowed value: ip_address
submissions
required

How many submissions the form holds.

object
completed_count
required

Submissions the respondent finished.

integer
>= -9007199254740991 <= 9007199254740991
partial_count
required

Partial responses captured before the respondent finished.

integer
>= -9007199254740991 <= 9007199254740991
urls
required

Where to open the form.

object
editor
required

The form in the fomr editor. Always present; opening it needs an app session, not a token.

string format: uri
share
required
Any of:
string format: uri
domain
required
Any of:
object
host
required

The custom domain of the share link, such as forms.example.com.

string
>= 4 characters <= 55 characters /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/
slug
required

The path after the host, starting with a slash.

string
>= 1 characters <= 100 characters /^[a-zA-Z0-9-_/-]+$/
status
required

Verification state of the domain: verified, pending, or failed.

string
Allowed values: verified pending failed
is_active
required

Whether the plan of the organization keeps the domain serving forms.

boolean
url
required

The link on the custom domain. It becomes urls.share once the domain is verified and active and a version is published.

string format: uri
preview_image_url
required
Any of:
string
created_at
required

When the form was created.

string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
updated_at
required

When the form was last changed.

string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/

Example

{
"status": "draft",
"access": {
"duplicate_response_rule": {
"type": "ip_address"
}
},
"domain": {
"status": "verified"
}
}

Default Response

Media typeapplication/json
object
code
required

The stable code to branch on.

string
Allowed values: INVALID_INPUT UNAUTHORIZED PERSONAL_ACCESS_TOKEN_EXPIRED PERSONAL_ACCESS_TOKEN_REVOKED PLAN_LIMIT_REACHED PRO_FEATURE_REQUIRED FORBIDDEN FORM_PUBLISH_EMAIL_UNVERIFIED ORGANIZATION_MEMBER_BLOCKED PERSONAL_ACCESS_TOKEN_NOT_PERMITTED NOT_FOUND FORM_NOT_PUBLISHED NO_DRAFT_DURING_PUBLISH FORM_ARCHIVED FORM_CONVERSION_BLOCKED FORM_PUBLISH_REVISION_CONFLICT PAYLOAD_TOO_LARGE FORM_PUBLISH_VALIDATION_FAILED FORM_SAFETY_REJECTED TOO_MANY_REQUESTS API_COOLDOWN SERVER AI_SERVICE_UNAVAILABLE SERVICE_UNAVAILABLE
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string
issues
required

Every value the request got wrong.

Array<object>
object
path
required

The path into the request, with property names and numeric array indexes.

Array
message
required

What is wrong with the value at this path.

string
code
required

The stable code for the issue. Branch on it, and use it with params to write your own text.

string
accepted_values

Accepted values when a node type or enum value is rejected.

Array
params

The values the message was built from, keyed by name, for clients that write their own text for each code.

object
key
additional properties
Any of:
string

Example

{
"code": "INVALID_INPUT"
}

Default Response

Media typeapplication/json
object
code
required

The stable code to branch on.

string
Allowed values: INVALID_INPUT UNAUTHORIZED PERSONAL_ACCESS_TOKEN_EXPIRED PERSONAL_ACCESS_TOKEN_REVOKED PLAN_LIMIT_REACHED PRO_FEATURE_REQUIRED FORBIDDEN FORM_PUBLISH_EMAIL_UNVERIFIED ORGANIZATION_MEMBER_BLOCKED PERSONAL_ACCESS_TOKEN_NOT_PERMITTED NOT_FOUND FORM_NOT_PUBLISHED NO_DRAFT_DURING_PUBLISH FORM_ARCHIVED FORM_CONVERSION_BLOCKED FORM_PUBLISH_REVISION_CONFLICT PAYLOAD_TOO_LARGE FORM_PUBLISH_VALIDATION_FAILED FORM_SAFETY_REJECTED TOO_MANY_REQUESTS API_COOLDOWN SERVER AI_SERVICE_UNAVAILABLE SERVICE_UNAVAILABLE
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string

Example

{
"code": "INVALID_INPUT"
}

A Plan Error: either an exhausted plan limit or a field that needs a larger plan. Branch on code.

Media typeapplication/json
Any of:
object
code
required

The organization is already at a limit its plan sets.

string
Allowed value: PLAN_LIMIT_REACHED
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string
plan
required

The plan whose limit was applied.

string
Allowed values: free pro
upgrade_url
required

Where to review plans and upgrade the organization.

string
limit
required

The limit that blocked the call.

object
key
required

Which plan limit was reached.

string
Allowed values: max_forms max_workspaces max_active_workflows_per_form
max
required

What the plan allows.

integer
>= -9007199254740991 <= 9007199254740991
current
required

What the organization is using right now.

integer
>= -9007199254740991 <= 9007199254740991

Example

{
"code": "PLAN_LIMIT_REACHED",
"plan": "free",
"limit": {
"key": "max_forms"
}
}

Default Response

Media typeapplication/json
object
code
required

The stable code to branch on.

string
Allowed values: INVALID_INPUT UNAUTHORIZED PERSONAL_ACCESS_TOKEN_EXPIRED PERSONAL_ACCESS_TOKEN_REVOKED PLAN_LIMIT_REACHED PRO_FEATURE_REQUIRED FORBIDDEN FORM_PUBLISH_EMAIL_UNVERIFIED ORGANIZATION_MEMBER_BLOCKED PERSONAL_ACCESS_TOKEN_NOT_PERMITTED NOT_FOUND FORM_NOT_PUBLISHED NO_DRAFT_DURING_PUBLISH FORM_ARCHIVED FORM_CONVERSION_BLOCKED FORM_PUBLISH_REVISION_CONFLICT PAYLOAD_TOO_LARGE FORM_PUBLISH_VALIDATION_FAILED FORM_SAFETY_REJECTED TOO_MANY_REQUESTS API_COOLDOWN SERVER AI_SERVICE_UNAVAILABLE SERVICE_UNAVAILABLE
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string
required_permission
required
Any of:
string

Example

{
"code": "INVALID_INPUT"
}

Default Response

Media typeapplication/json
object
code
required

Returned for a form that does not exist, is in Trash, or belongs to another organization. The three are not told apart.

string
Allowed value: NOT_FOUND
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string

Example

{
"code": "NOT_FOUND"
}

A form that cannot be published as it stands: it is archived, or it holds no draft. Branch on code.

Media typeapplication/json
Any of:
object
code
required

The form is archived: it has no editable draft, so draft writes, publish, and conversion are refused.

string
Allowed value: FORM_ARCHIVED
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string

Example

{
"code": "FORM_ARCHIVED"
}

A publish the server refused: the draft has Publish Issues, or safety checks flagged it. Branch on code.

Media typeapplication/json
Any of:
object
code
required

The draft is not complete enough to publish. Nothing was written.

string
Allowed value: FORM_PUBLISH_VALIDATION_FAILED
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string
issues
required

Every Publish Issue that blocked the publish. The same list validate returns.

Array<object>
object
code
required

The stable Publish Issue code to branch on. Besides the draft checks, validate reports FORM_PUBLISH_EMAIL_UNVERIFIED, FORM_SAFETY_REJECTED, and PRO_FEATURE_REQUIRED for what publish would refuse. New codes are added over time.

string
Allowed values: FORM_PUBLISH_DRAFT_INVALID FORM_PUBLISH_BLUEPRINT_INVALID FORM_PUBLISH_BLUEPRINT_TOO_LARGE FORM_PUBLISH_TOO_MANY_PAGES FORM_PUBLISH_TOO_MANY_NODES FORM_PUBLISH_TOO_MANY_PAGE_NODES FORM_PUBLISH_TOO_MANY_OPTIONS FORM_PUBLISH_TOO_MANY_TOTAL_OPTIONS FORM_PUBLISH_TOO_MANY_MATRIX_ROWS FORM_PUBLISH_TOO_MANY_MATRIX_COLUMNS FORM_PUBLISH_MISSING_FINAL_PAGE FORM_PUBLISH_MULTIPLE_FINAL_PAGES FORM_PUBLISH_FINAL_PAGE_POSITION FORM_PUBLISH_FINAL_PAGE_HIDDEN FORM_PUBLISH_ALL_PAGES_HIDDEN FORM_PUBLISH_FINAL_PAGE_NODES FORM_PUBLISH_THANK_YOU_PLACEMENT FORM_PUBLISH_MISSING_QUESTION FORM_PUBLISH_MULTIPLE_ENTRYPOINTS FORM_PUBLISH_ENTRYPOINT_FINAL_PAGE FORM_PUBLISH_ENTRYPOINT_POSITION FORM_PUBLISH_AUTO_JUMP_INVALID FORM_PUBLISH_MISSING_OPTIONS FORM_PUBLISH_MISSING_MATRIX_ROWS FORM_PUBLISH_NUMBER_SETTINGS_INVALID FORM_PUBLISH_DANGLING_PIPE FORM_PUBLISH_EMAIL_UNVERIFIED FORM_SAFETY_REJECTED PRO_FEATURE_REQUIRED
message
required

What to fix, in words. Not part of the contract; never branch on it.

string
path
required

The path into the Draft Document, with property names and numeric array indexes, such as [“pages”, 1, “nodes”, 0, “meta”, “select_options_data”]. Empty when the issue is about the form as a whole.

Array
page_public_id
required
Any of:
string
>= 4 characters <= 4 characters /^[A-Za-z0-9_-]+$/
node_public_id
required
Any of:
string
>= 4 characters <= 4 characters /^[A-Za-z0-9_-]+$/

Example

{
"code": "FORM_PUBLISH_VALIDATION_FAILED",
"issues": [
{
"code": "FORM_PUBLISH_DRAFT_INVALID"
}
]
}

A rejected call. The organization budget names the policy it rejected the call under; the request floor that bounds hammering sends the envelope alone.

Media typeapplication/json
Any of:
object
code
required

The stable code to branch on.

string
Allowed values: INVALID_INPUT UNAUTHORIZED PERSONAL_ACCESS_TOKEN_EXPIRED PERSONAL_ACCESS_TOKEN_REVOKED PLAN_LIMIT_REACHED PRO_FEATURE_REQUIRED FORBIDDEN FORM_PUBLISH_EMAIL_UNVERIFIED ORGANIZATION_MEMBER_BLOCKED PERSONAL_ACCESS_TOKEN_NOT_PERMITTED NOT_FOUND FORM_NOT_PUBLISHED NO_DRAFT_DURING_PUBLISH FORM_ARCHIVED FORM_CONVERSION_BLOCKED FORM_PUBLISH_REVISION_CONFLICT PAYLOAD_TOO_LARGE FORM_PUBLISH_VALIDATION_FAILED FORM_SAFETY_REJECTED TOO_MANY_REQUESTS API_COOLDOWN SERVER AI_SERVICE_UNAVAILABLE SERVICE_UNAVAILABLE
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string
policy
required

The policy that rejected the call.

string
Allowed values: minute burst
plan
required

The plan whose numbers were applied.

string
Allowed values: free pro
limit
required

The policy limit in credits.

integer
>= -9007199254740991 <= 9007199254740991
used
required

Credits already spent in the open window.

integer
>= -9007199254740991 <= 9007199254740991
remaining
required

Credits still available under the policy.

integer
>= -9007199254740991 <= 9007199254740991
cost
required

What the rejected call would have cost.

integer
>= -9007199254740991 <= 9007199254740991
window_seconds
required

How long the policy window lasts.

integer
>= -9007199254740991 <= 9007199254740991
cooldown_until
required
Any of:
string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
resets_at
required

When the window that rejected the call ends.

string format: date-time
/^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$/
retry_after_seconds
required

Seconds to wait before retrying. Also sent as the Retry-After response header.

integer
>= -9007199254740991 <= 9007199254740991
upgrade_url
required
Any of:
string

Example

{
"code": "INVALID_INPUT",
"policy": "minute",
"plan": "free"
}

Default Response

Media typeapplication/json
object
code
required

The stable code to branch on.

string
Allowed values: INVALID_INPUT UNAUTHORIZED PERSONAL_ACCESS_TOKEN_EXPIRED PERSONAL_ACCESS_TOKEN_REVOKED PLAN_LIMIT_REACHED PRO_FEATURE_REQUIRED FORBIDDEN FORM_PUBLISH_EMAIL_UNVERIFIED ORGANIZATION_MEMBER_BLOCKED PERSONAL_ACCESS_TOKEN_NOT_PERMITTED NOT_FOUND FORM_NOT_PUBLISHED NO_DRAFT_DURING_PUBLISH FORM_ARCHIVED FORM_CONVERSION_BLOCKED FORM_PUBLISH_REVISION_CONFLICT PAYLOAD_TOO_LARGE FORM_PUBLISH_VALIDATION_FAILED FORM_SAFETY_REJECTED TOO_MANY_REQUESTS API_COOLDOWN SERVER AI_SERVICE_UNAVAILABLE SERVICE_UNAVAILABLE
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string

Example

{
"code": "INVALID_INPUT"
}

Default Response

Media typeapplication/json
object
code
required

The requested operation has no available capacity. Retry after the Retry-After header says.

string
Allowed value: SERVICE_UNAVAILABLE
message
required

A human-readable explanation. Not part of the contract; never branch on it.

string
request_id
required

The ID of the failed request, also sent as the X-Request-Id response header.

string
retry_after_seconds
required

Seconds to wait before retrying. Also sent as the Retry-After response header.

integer
>= -9007199254740991 <= 9007199254740991

Example

{
"code": "SERVICE_UNAVAILABLE"
}

Your next form could be the best-looking thing on your site.

Free forever. Unlimited responses. No signup to start.