# Publish the draft to the gallery now, with no review, as the next version

`POST https://api.instacloud.com/orgs/{orgId}/templates/{code}/publish`

**Token scope:** `account` · `org` — read-only tokens refused (not a GET).

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgId` | path | `string` | yes |  |
| `code` | path | `string` | yes |  |

## Request body (application/json)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `expectedUpdatedAt` | `string` | yes | The draft's updatedAt as the client rendered it. A different current value answers 409 template_draft_changed. |

## Response 200

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `template` | `object` | yes |  |
| `template.id` | `string` | yes | The draft's stable id. A rename of its code keeps it, and a new draft gets a new one, even under a code a deleted draft had. Routes and editor links address a draft by its code. |
| `template.code` | `string` | yes |  |
| `template.orgId` | `string` | yes |  |
| `template.sourceProjectId` | `string \| null` | yes |  |
| `template.blank` | `boolean` | yes | Created with POST /orgs/{orgId}/templates, with no project behind it: every service is one the author added, and Regenerate From Project answers 400. A generated draft whose project was deleted is not blank. |
| `template.status` | `"draft" \| "published" \| "unpublished"` | yes |  |
| `template.publishedVersion` | `string \| null` | yes |  |
| `template.hasUnpublishedChanges` | `boolean` | yes |  |
| `template.name` | `string` | yes |  |
| `template.tagline` | `string \| null` | yes |  |
| `template.category` | `string \| null` | yes |  |
| `template.readme` | `string \| null` | yes |  |
| `template.defaultReadme` | `string` | yes | A README skeleton built from the draft as it is now: service names and required variable names only, never a value. The console offers it behind a button; it is not stored. |
| `template.logoUrl` | `string \| null` | yes |  |
| `template.services` | `object[]` | yes |  |
| `template.services.name` | `string` | yes |  |
| `template.services.type` | `string` | yes | web with a port, worker without one, or the managed type (postgres, redis, mysql, mongodb or storage). It and the fields below are the values after the author's settings. |
| `template.services.image` | `string \| null` | yes |  |
| `template.services.port` | `integer \| null` | yes |  |
| `template.services.healthcheck` | `string \| null` | yes |  |
| `template.services.volume` | `boolean` | yes |  |
| `template.services.mountPath` | `string \| null` | yes | null with volume true: the volume mounts at the default /data. null with volume false: no volume. |
| `template.services.command` | `string \| null` | yes |  |
| `template.services.alwaysOn` | `boolean \| null` | yes |  |
| `template.services.public` | `boolean` | no | Storage only, always present on a bucket: anonymous public-read, after the author's setting. |
| `template.services.pgVersion` | `integer` | no | Postgres only: the major a deploy creates the database on, after the author's setting. Absent when the project's database recorded none. |
| `template.services.added` | `boolean` | no | Present, and true, only on a service the author added: a PATCH deletes it for good. One from the project is removed instead. |
| `template.services.removed` | `boolean` | yes | The author left this service out of the template. It stays listed so it can be restored. |
| `template.services.variables` | `object[]` | yes | Sorted by the generator's name (originalName when renamed). Empty for a managed database. A removed service lists what restoring it brings back. |
| `template.services.variables.name` | `string` | yes |  |
| `template.services.variables.kind` | `"required" \| "optional" \| "default" \| "reference" \| "generated" \| "fixed" \| "removed"` | yes |  |
| `template.services.variables.value` | `string` | no | default: the default text. reference: the token. fixed: the literal or the address reference. |
| `template.services.variables.description` | `string` | no | What whoever deploys reads. Omitted while it is the default sentence, which the console shows as an empty box. |
| `template.services.variables.brokenReference` | `string` | no | The token this variable referenced before its service was removed from the template or left the project. The variable is then required. |
| `template.services.variables.originalName` | `string` | no | Present only when the author renamed the variable: the generator's name, which a PATCH variables entry sends as name. name is then the new one. |
| `template.services.variables.added` | `boolean` | no | Present, and true, only on a variable the author added: a PATCH deletes it for good. One from the project is removed with choice removed instead. |
| `template.referenceOptions` | `object[]` | yes | What a variable may reference in the template as it is now. A removed service offers nothing. |
| `template.referenceOptions.token` | `string` | yes | Exactly what a reference choice sends and the manifest stores. |
| `template.referenceOptions.service` | `string` | yes |  |
| `template.referenceOptions.key` | `string` | yes | A credential key of a managed database or a storage bucket, or url or host of a web service. |
| `template.editableFields` | `object` | yes | Per service type, the TemplateServiceSettings keys a template service of that type takes. A PATCH answers 400 for any other. |
| `template.editableFields.web` | `"image" \| "port" \| "healthcheck" \| "command" \| "mountPath" \| "alwaysOn"[]` | yes |  |
| `template.editableFields.worker` | `"image" \| "port" \| "healthcheck" \| "command" \| "mountPath" \| "alwaysOn"[]` | yes |  |
| `template.editableFields.postgres` | `"pgVersion"[]` | yes |  |
| `template.editableFields.redis` | `string[]` | yes |  |
| `template.editableFields.mysql` | `string[]` | yes |  |
| `template.editableFields.mongodb` | `string[]` | yes |  |
| `template.editableFields.storage` | `"public"[]` | yes |  |
| `template.referenceKeys` | `object` | yes | Per managed service type, the credential keys a variable may reference on a service of that type, one the author has not applied yet included (secretNames.ts CANONICAL_KEYS). A web service's url and host are not listed. |
| `template.referenceKeys.postgres` | `string[]` | yes |  |
| `template.referenceKeys.redis` | `string[]` | yes |  |
| `template.referenceKeys.mysql` | `string[]` | yes |  |
| `template.referenceKeys.mongodb` | `string[]` | yes |  |
| `template.referenceKeys.storage` | `string[]` | yes |  |
| `template.publishRequirements` | `object[]` | yes | Always seven, in this order: has_service, no_blocked, no_conflicts, has_tagline, has_category, has_readme, required_descriptions. |
| `template.publishRequirements.code` | `"has_service" \| "no_blocked" \| "no_conflicts" \| "has_tagline" \| "has_category" \| "has_readme" \| "required_descriptions"` | yes |  |
| `template.publishRequirements.ok` | `boolean` | yes |  |
| `template.publishRequirements.items` | `string[]` | yes | no_blocked: each blocked reason. no_conflicts: one sentence with its fix for each name the author added that the project now has too, services first. required_descriptions: each <service>.<VAR> still without a description. Otherwise empty. |
| `template.report` | `object` | yes | What generation skipped, what blocks publishing (each with a fix), and notices. |
| `template.report.skipped` | `object[]` | yes |  |
| `template.report.skipped.service` | `string` | no |  |
| `template.report.skipped.variable` | `string` | no |  |
| `template.report.skipped.reason` | `string` | yes |  |
| `template.report.skipped.fix` | `string` | no |  |
| `template.report.blocked` | `object[]` | yes |  |
| `template.report.blocked.service` | `string` | no |  |
| `template.report.blocked.variable` | `string` | no |  |
| `template.report.blocked.reason` | `string` | yes |  |
| `template.report.blocked.fix` | `string` | no |  |
| `template.report.notices` | `object[]` | yes |  |
| `template.report.notices.service` | `string` | no |  |
| `template.report.notices.variable` | `string` | no |  |
| `template.report.notices.reason` | `string` | yes |  |
| `template.report.notices.fix` | `string` | no |  |
| `template.updatedAt` | `string` | yes |  |
| `template.takenDown` | `boolean` | yes | Staff took this template down. It cannot be republished. |
| `template.deployCount` | `integer` | yes | Succeeded deployments of this template. |
| `template.totalProjects` | `integer` | yes | Distinct projects that deployed this template since its first publish, any outcome: the number the gallery card shows (the catalog's totalProjects). |

## Response 400

code template_not_ready: a publish requirement is unmet. The message names the first, publishRequirements in the view lists them all. Without a code: a malformed request, a template that does not pass validation, or a draft generated by an older version.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |
| `code` | `string` | no | stable machine-readable reason, e.g. private_lane_disabled, private_lane_unavailable_in_region, public_access_closed, credential_change_in_progress |

## Response 404

Default Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |

## Response 409

code template_publish_conflict: the published version changed outside this draft. code template_code_owned: the code belongs to someone else. code template_taken_down: InstaCloud took this template down and it cannot be republished. code template_draft_changed: the draft changed since the client read it (expectedUpdatedAt), review it and publish again.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `error` | `string` | yes |  |
| `code` | `string` | no | stable machine-readable reason, e.g. private_lane_disabled, private_lane_unavailable_in_region, public_access_closed, credential_change_in_progress |
