> For the complete documentation index, see [llms.txt](https://developer.celigo.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.celigo.com/api/api-reference/marketplace-and-templates/templates.md).

# Templates

Templates are pre-built integration configurations that Partners publish to the Celigo marketplace for other users to install.

The template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.

Creating, updating, uploading and publishing templates requires a **Partner account** — non-partner callers receive 403 `access_restricted`. Installing a template (`/v1/integrations/template` routes) needs install permission instead: the account owner, administrators and manage users have it, and monitor or integration-level users only when their access allows creating integrations.

### Template schema

## The Template object

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"components":{"schemas":{"Template":{"type":"object","required":["_id","name","lastModified"],"description":"A template resource as returned by the API.\n\nFields that are `false` or empty may be omitted from the response\n(e.g. `published` is omitted when the template is unpublished,\n`imageURL` is omitted when not set).","allOf":[{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"type":{"type":"string","enum":["integration","mcpserver"],"description":"Kind of resource the template installs, which decides the install\nroute. Assigned by the server from the uploaded zip (a root\n`mcpserver.json` makes it `mcpserver`, a root `integration.json`\nmakes it `integration`) and not settable through this API; present\nonce a zip has been uploaded."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Omitted when never\nset; may be an empty string when the field was cleared."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. May be\nan empty string when the field was cleared; legacy values may\nlack a scheme (e.g. `www.example.com`)."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Whether the template is published to the marketplace. Omitted\nfrom the response when `false`. Can only be set to `true` after\na template zip file has been uploaded via `GET /v1/s3SignedURL`."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers associated with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."},"free":{"type":"boolean","description":"When true, the template is free to install with no license requirement.","readOnly":true},"numInstalls":{"type":"integer","description":"Number of times this template has been installed.","readOnly":true,"minimum":0}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]}}}}
```

## List templates

> Returns all templates owned by the authenticated Partner account.\
> The response is a flat JSON array with no pagination.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"parameters":{"After":{"name":"after","in":"query","required":false,"description":"Opaque cursor for forward pagination. Pass the value from the `Link`\nresponse header (`rel=\"next\"`) to fetch the next page.","schema":{"type":"string"}}},"schemas":{"Template":{"type":"object","required":["_id","name","lastModified"],"description":"A template resource as returned by the API.\n\nFields that are `false` or empty may be omitted from the response\n(e.g. `published` is omitted when the template is unpublished,\n`imageURL` is omitted when not set).","allOf":[{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"type":{"type":"string","enum":["integration","mcpserver"],"description":"Kind of resource the template installs, which decides the install\nroute. Assigned by the server from the uploaded zip (a root\n`mcpserver.json` makes it `mcpserver`, a root `integration.json`\nmakes it `integration`) and not settable through this API; present\nonce a zip has been uploaded."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Omitted when never\nset; may be an empty string when the field was cleared."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. May be\nan empty string when the field was cleared; legacy values may\nlack a scheme (e.g. `www.example.com`)."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Whether the template is published to the marketplace. Omitted\nfrom the response when `false`. Can only be set to `true` after\na template zip file has been uploaded via `GET /v1/s3SignedURL`."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers associated with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."},"free":{"type":"boolean","description":"When true, the template is free to install with no license requirement.","readOnly":true},"numInstalls":{"type":"integer","description":"Number of times this template has been installed.","readOnly":true,"minimum":0}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/templates":{"get":{"operationId":"listTemplates","tags":["Templates"],"parameters":[{"name":"limit","in":"query","description":"Maximum number of records to return per page.","schema":{"type":"integer","minimum":1}},{"$ref":"#/components/parameters/After"}],"summary":"List templates","description":"Returns all templates owned by the authenticated Partner account.\nThe response is a flat JSON array with no pagination.","responses":{"200":{"description":"Array of template resources.","headers":{"Link":{"description":"RFC-5988 pagination links. When more pages remain, includes a `<...>; rel=\"next\"` entry;\nabsent on the final page.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"}}}}}}
```

## Create a template

> Creates a new integration template. Only \`name\` is required; all\
> other fields are optional. Upload a zip via \`GET /v1/s3SignedURL\`\
> before attempting to publish.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"TemplateInput":{"type":"object","description":"Request body for creating or updating a template. `name` is always\nrequired, even on PUT. `published` can only be set to `true` after\nuploading a template zip via `GET /v1/s3SignedURL`.","required":["name"],"properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Send an empty string to\nclear it."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. Send an\nempty string to clear it."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Set to `true` to publish the template to the marketplace (requires\na zip upload first). Set to `false` to unpublish."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers to associate with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."}}},"Template":{"type":"object","required":["_id","name","lastModified"],"description":"A template resource as returned by the API.\n\nFields that are `false` or empty may be omitted from the response\n(e.g. `published` is omitted when the template is unpublished,\n`imageURL` is omitted when not set).","allOf":[{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"type":{"type":"string","enum":["integration","mcpserver"],"description":"Kind of resource the template installs, which decides the install\nroute. Assigned by the server from the uploaded zip (a root\n`mcpserver.json` makes it `mcpserver`, a root `integration.json`\nmakes it `integration`) and not settable through this API; present\nonce a zip has been uploaded."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Omitted when never\nset; may be an empty string when the field was cleared."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. May be\nan empty string when the field was cleared; legacy values may\nlack a scheme (e.g. `www.example.com`)."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Whether the template is published to the marketplace. Omitted\nfrom the response when `false`. Can only be set to `true` after\na template zip file has been uploaded via `GET /v1/s3SignedURL`."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers associated with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."},"free":{"type":"boolean","description":"When true, the template is free to install with no license requirement.","readOnly":true},"numInstalls":{"type":"integer","description":"Number of times this template has been installed.","readOnly":true,"minimum":0}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422-unprocessable-entity":{"description":"Unprocessable entity. The request was well-formed but was unable to be followed due to semantic errors.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/templates":{"post":{"operationId":"createTemplate","tags":["Templates"],"summary":"Create a template","description":"Creates a new integration template. Only `name` is required; all\nother fields are optional. Upload a zip via `GET /v1/s3SignedURL`\nbefore attempting to publish.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateInput"}}}},"responses":{"201":{"description":"Template created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"422":{"$ref":"#/components/responses/422-unprocessable-entity"}}}}}}
```

## Get a template

> Returns a single template by ID.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Template":{"type":"object","required":["_id","name","lastModified"],"description":"A template resource as returned by the API.\n\nFields that are `false` or empty may be omitted from the response\n(e.g. `published` is omitted when the template is unpublished,\n`imageURL` is omitted when not set).","allOf":[{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"type":{"type":"string","enum":["integration","mcpserver"],"description":"Kind of resource the template installs, which decides the install\nroute. Assigned by the server from the uploaded zip (a root\n`mcpserver.json` makes it `mcpserver`, a root `integration.json`\nmakes it `integration`) and not settable through this API; present\nonce a zip has been uploaded."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Omitted when never\nset; may be an empty string when the field was cleared."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. May be\nan empty string when the field was cleared; legacy values may\nlack a scheme (e.g. `www.example.com`)."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Whether the template is published to the marketplace. Omitted\nfrom the response when `false`. Can only be set to `true` after\na template zip file has been uploaded via `GET /v1/s3SignedURL`."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers associated with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."},"free":{"type":"boolean","description":"When true, the template is free to install with no license requirement.","readOnly":true},"numInstalls":{"type":"integer","description":"Number of times this template has been installed.","readOnly":true,"minimum":0}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/templates/{_id}":{"get":{"operationId":"getTemplateById","tags":["Templates"],"summary":"Get a template","description":"Returns a single template by ID.","parameters":[{"name":"_id","in":"path","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"200":{"description":"The template resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Update a template

> Replaces the template's fields with the values in the request body.\
> Omitted optional fields retain their current values. \`name\` is\
> required on every PUT, even if unchanged.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"TemplateInput":{"type":"object","description":"Request body for creating or updating a template. `name` is always\nrequired, even on PUT. `published` can only be set to `true` after\nuploading a template zip via `GET /v1/s3SignedURL`.","required":["name"],"properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Send an empty string to\nclear it."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. Send an\nempty string to clear it."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Set to `true` to publish the template to the marketplace (requires\na zip upload first). Set to `false` to unpublish."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers to associate with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."}}},"Template":{"type":"object","required":["_id","name","lastModified"],"description":"A template resource as returned by the API.\n\nFields that are `false` or empty may be omitted from the response\n(e.g. `published` is omitted when the template is unpublished,\n`imageURL` is omitted when not set).","allOf":[{"$ref":"#/components/schemas/ResourceResponse"},{"type":"object","properties":{"name":{"type":"string","maxLength":100,"description":"Display name of the template."},"type":{"type":"string","enum":["integration","mcpserver"],"description":"Kind of resource the template installs, which decides the install\nroute. Assigned by the server from the uploaded zip (a root\n`mcpserver.json` makes it `mcpserver`, a root `integration.json`\nmakes it `integration`) and not settable through this API; present\nonce a zip has been uploaded."},"description":{"type":"string","description":"Longer description shown in the marketplace."},"imageURL":{"type":"string","description":"URL of the template's marketplace image. Omitted when never\nset; may be an empty string when the field was cleared."},"websiteURL":{"type":"string","description":"URL to the partner's website or template landing page. May be\nan empty string when the field was cleared; legacy values may\nlack a scheme (e.g. `www.example.com`)."},"contactEmail":{"type":"string","description":"Contact email(s) for template support — a single address or a\ncomma-separated list of addresses."},"published":{"type":"boolean","description":"Whether the template is published to the marketplace. Omitted\nfrom the response when `false`. Can only be set to `true` after\na template zip file has been uploaded via `GET /v1/s3SignedURL`."},"applications":{"type":"array","items":{"type":"string"},"description":"Application identifiers associated with this template.\nValues may be application slugs (e.g. `salesforce`) or\nMongo ObjectIds."},"free":{"type":"boolean","description":"When true, the template is free to install with no license requirement.","readOnly":true},"numInstalls":{"type":"integer","description":"Number of times this template has been installed.","readOnly":true,"minimum":0}}}]},"ResourceResponse":{"type":"object","description":"Response","properties":{"_id":{"type":"string","format":"objectId","readOnly":true,"description":"Unique identifier for the resource. Format is a 24-character hexadecimal string."},"createdAt":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was created. Set automatically and cannot be modified."},"lastModified":{"type":"string","format":"date-time","readOnly":true,"description":"Timestamp when the resource was last updated. Changes whenever any property is modified."},"deletedAt":{"type":["string","null"],"format":"date-time","readOnly":true,"description":"Timestamp when the resource was soft-deleted. When null or absent, the resource is active."}},"required":["_id"]},"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"400-bad-request":{"description":"Bad request. The server could not understand the request because of malformed syntax or invalid parameters.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/templates/{_id}":{"put":{"operationId":"updateTemplate","tags":["Templates"],"summary":"Update a template","description":"Replaces the template's fields with the values in the request body.\nOmitted optional fields retain their current values. `name` is\nrequired on every PUT, even if unchanged.","parameters":[{"name":"_id","in":"path","required":true,"schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateInput"}}}},"responses":{"200":{"description":"Updated template.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"400":{"$ref":"#/components/responses/400-bad-request"},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"},"422":{"description":"Template zip has not been uploaded yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Delete a template

> Permanently deletes a template.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-forbidden":{"description":"Forbidden. The authenticated caller does not have permission to perform this operation.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404-not-found":{"description":"Not found. The requested resource does not exist or is not visible to the caller.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}}},"paths":{"/v1/templates/{_id}":{"delete":{"operationId":"deleteTemplate","tags":["Templates"],"summary":"Delete a template","description":"Permanently deletes a template.","parameters":[{"name":"_id","in":"path","required":true,"schema":{"type":"string","format":"objectId"}}],"responses":{"204":{"description":"Template deleted."},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-forbidden"},"404":{"$ref":"#/components/responses/404-not-found"}}}}}}
```

## Install an integration from a template

> Installs an integration from a published template. The caller provides a\
> \`connectionMap\` that maps each template-defined connection ID to an actual\
> connection ID in the caller's account. Every template connection must\
> appear as a key in \`connectionMap\`.\
> \
> Only templates whose \`type\` is \`integration\` install here. A template\
> whose \`type\` is \`mcpserver\` is rejected before anything is created —\
> install it with \`POST /v1/mcpservers/template/{\_templateId}\` instead.\
> Check \`type\` on the \`GET /v1/templates/{\_id}/preview\` response first.\
> \
> This one-shot install doesn't run a template's own install steps. A\
> template whose preview \`Integration\` object lists a \`template\_zip\`\
> install step (most published templates have one) is refused with 422\
> and nothing is created; install it from the integrator.io marketplace,\
> whose installer runs those steps.\
> \
> Every request that reaches the platform starts a new install — there is\
> no idempotency key, so a retry after a timeout or an ambiguous failure\
> creates a second integration with its own flows, exports and imports.\
> Before retrying, look for the integration the first request created: one\
> carrying this template's \`\_templateId\`, named like the preview's\
> \`Integration\` object, with a \`createdAt\` near the request.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-cannot-install":{"description":"The caller may not install templates. The account owner, administrators and manage users\ncan; a monitor user or a user with access to specific integrations only can install a\ntemplate only when their access allows creating integrations\n(`allowIntegrationWorkspaceCreation`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/integrations/template/{_id}":{"post":{"operationId":"installTemplate","tags":["Templates"],"summary":"Install an integration from a template","description":"Installs an integration from a published template. The caller provides a\n`connectionMap` that maps each template-defined connection ID to an actual\nconnection ID in the caller's account. Every template connection must\nappear as a key in `connectionMap`.\n\nOnly templates whose `type` is `integration` install here. A template\nwhose `type` is `mcpserver` is rejected before anything is created —\ninstall it with `POST /v1/mcpservers/template/{_templateId}` instead.\nCheck `type` on the `GET /v1/templates/{_id}/preview` response first.\n\nThis one-shot install doesn't run a template's own install steps. A\ntemplate whose preview `Integration` object lists a `template_zip`\ninstall step (most published templates have one) is refused with 422\nand nothing is created; install it from the integrator.io marketplace,\nwhose installer runs those steps.\n\nEvery request that reaches the platform starts a new install — there is\nno idempotency key, so a retry after a timeout or an ambiguous failure\ncreates a second integration with its own flows, exports and imports.\nBefore retrying, look for the integration the first request created: one\ncarrying this template's `_templateId`, named like the preview's\n`Integration` object, with a `createdAt` near the request.","parameters":[{"name":"_id","in":"path","required":true,"description":"The template ID to install from.","schema":{"type":"string","format":"objectId"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Connection mapping from template connection IDs to the caller's\nreal connection IDs.","properties":{"connectionMap":{"type":"object","description":"Keys are connection `_id` values from the template; values are\nthe caller's own connection `_id` values that should replace\nthem during install.","additionalProperties":{"type":"string"}},"_stackId":{"type":"string","format":"objectId","description":"Stack for the template's hooks and wrapper connections that run on a stack.\nRequired when the template preview reports `stackRequired: true`: without\nit the install fails with 400 `stack_not_found`. The stack must belong to\nthe environment or be shared with it (404 `stack_not_found` for an unknown\nid, 400 `invalid_stack` otherwise)."}},"required":["connectionMap"]}}}},"responses":{"201":{"description":"Template installed. Returns one entry per resource the install created.","content":{"application/json":{"schema":{"type":"array","description":"The resources the install created, the new integration among them.","items":{"type":"object","properties":{"model":{"type":"string","description":"Resource type of the created document (`Integration`, `Flow`, `Export`, `Import`, ...)."},"_id":{"type":"string","format":"objectId","description":"Id of the created document."}},"additionalProperties":true}}}}},"400":{"description":"Missing or empty `connectionMap`; the template is an MCP server\ntemplate that must be installed through\n`POST /v1/mcpservers/template/{_templateId}`; the template needs a\nstack and the body has no `_stackId` (`stack_not_found`); or the\n`_stackId` names a stack that neither belongs to this environment nor\nis shared with it (`invalid_stack`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-cannot-install"},"404":{"description":"Template not found, or no stack has the `_stackId` sent (`stack_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Connection IDs in `connectionMap` do not match the template's\nexpected connection IDs, or the template's install steps include a\n`template_zip` step, which this install doesn't run. Nothing is created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Preview a template install from an uploaded zip

> Previews the resources that would be created by installing a template\
> from an uploaded zip file. The \`runKey\` query parameter ties this request\
> to a previously uploaded zip (obtained from \`GET /v1/s3SignedURL\`).

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-cannot-install":{"description":"The caller may not install templates. The account owner, administrators and manage users\ncan; a monitor user or a user with access to specific integrations only can install a\ntemplate only when their access allows creating integrations\n(`allowIntegrationWorkspaceCreation`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/integrations/template/preview":{"get":{"operationId":"previewTemplateInstall","tags":["Templates"],"summary":"Preview a template install from an uploaded zip","description":"Previews the resources that would be created by installing a template\nfrom an uploaded zip file. The `runKey` query parameter ties this request\nto a previously uploaded zip (obtained from `GET /v1/s3SignedURL`).","parameters":[{"name":"runKey","in":"query","required":true,"description":"The `runKey` returned by `GET /v1/s3SignedURL` after uploading the\ntemplate zip file.","schema":{"type":"string"}}],"responses":{"200":{"description":"Preview of resources the template would create.","content":{"application/json":{"schema":{"type":"object","description":"Preview envelope with resource docs.","additionalProperties":true}}}},"400":{"description":"Missing required `runKey` query parameter.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-cannot-install"},"404":{"description":"The `runKey` does not match any uploaded zip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```

## Install a template from an uploaded zip

> Installs a template from a previously uploaded zip file. The \`runKey\`\
> in the request body ties this request to the zip upload (obtained from\
> \`GET /v1/s3SignedURL\`).\
> \
> The zip carries connection definitions without credentials, and the\
> installed integration receives new connection \`\_id\`s. Configure each\
> connection in the new integration before running its flows; connection\
> \`\_id\`s that existed only in the zip are not preserved.

```json
{"openapi":"3.2.0","info":{"title":"Templates","version":"1.0.0"},"tags":[{"name":"Templates","description":"Templates are pre-built integration configurations that Partners\npublish to the Celigo marketplace for other users to install.\n\nThe template lifecycle is: create → upload zip (via `GET /v1/s3SignedURL`) → publish.\n\nCreating, updating, uploading and publishing templates requires a\n**Partner account** — non-partner callers receive 403\n`access_restricted`. Installing a template (`/v1/integrations/template`\nroutes) needs install permission instead: the account owner,\nadministrators and manage users have it, and monitor or\nintegration-level users only when their access allows creating\nintegrations.\n\n## Template schema\n\n{% openapi-schemas spec=\"template\" schemas=\"Template\" grouped=\"true\" %}"}],"servers":[{"url":"https://api.integrator.io","description":"Production (US / default region)"},{"url":"https://api.eu.integrator.io","description":"Production (EU region)"},{"url":"https://api.au.integrator.io","description":"Production (AU region)"},{"url":"https://api.ca.integrator.io","description":"Production (CA region)"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","description":"Standard error response envelope returned by integrator.io APIs.","properties":{"errors":{"type":"array","description":"List of errors that occurred while processing the request.","items":{"type":"object","properties":{"code":{"oneOf":[{"type":"string"},{"type":"integer"}],"description":"Machine-readable error code. Usually a string like\n`invalid_ref`, `missing_required_field`, or `unauthorized`;\nmay be an **integer** when the error mirrors an upstream HTTP\nstatus (e.g. `500`) — most commonly returned by connection-ping\nand adaptor-proxy responses."},"message":{"type":"string","description":"Human-readable description of the error."},"field":{"type":"string","description":"Optional pointer to the document field that caused the error.\nUsed by structural validation errors (`missing_required_field`,\n`invalid_ref`) to indicate which field is at fault\n(e.g. `_id`, `type`, `http.baseURI`)."},"source":{"type":"string","description":"Optional origin layer for the error — e.g. `application` when\nthe error came from the remote system the adaptor called,\n`connector` when the adaptor itself rejected the request."}},"required":["message"]}}},"required":["errors"]}},"responses":{"401-unauthorized":{"description":"Unauthorized. The request lacks a valid bearer token, or the provided token\nfailed to authenticate.\n\nNote: the 401 response is produced by the auth middleware **before** the\nrequest reaches the endpoint handler, so it does **not** follow the\nstandard `{errors: [...]}` envelope. Instead the body is a bare\n`{message: string}` object with no `code`, no `errors` array. Callers\nhandling 401s should key off the HTTP status and the `message` string,\nnot try to destructure an `errors[]`.","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Human-readable description of the auth failure. Known values:\n- `\"Unauthorized\"` — no `Authorization` header on the request.\n- `\"Bearer Authentication Failed\"` — header present but token\n  is invalid, revoked, or expired."}},"required":["message"]}}}},"403-cannot-install":{"description":"The caller may not install templates. The account owner, administrators and manage users\ncan; a monitor user or a user with access to specific integrations only can install a\ntemplate only when their access allows creating integrations\n(`allowIntegrationWorkspaceCreation`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/v1/integrations/template":{"post":{"operationId":"installTemplateFromZip","tags":["Templates"],"summary":"Install a template from an uploaded zip","description":"Installs a template from a previously uploaded zip file. The `runKey`\nin the request body ties this request to the zip upload (obtained from\n`GET /v1/s3SignedURL`).\n\nThe zip carries connection definitions without credentials, and the\ninstalled integration receives new connection `_id`s. Configure each\nconnection in the new integration before running its flows; connection\n`_id`s that existed only in the zip are not preserved.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"The `runKey` from the zip upload.","properties":{"runKey":{"type":"string","description":"The `runKey` returned by `GET /v1/s3SignedURL` after uploading\nthe template zip file."},"_stackId":{"type":"string","format":"objectId","description":"Stack for the template's hooks and wrapper connections that run on a stack.\nRequired when the preview reports `stackRequired: true` (400\n`stack_not_found` without it)."}},"required":["runKey"]}}}},"responses":{"200":{"description":"Template installed from uploaded zip.","content":{"application/json":{"schema":{"type":"object","description":"The created integration and related resources.","additionalProperties":true}}}},"400":{"description":"Missing required `runKey` in request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/401-unauthorized"},"403":{"$ref":"#/components/responses/403-cannot-install"},"404":{"description":"The `runKey` does not match any uploaded zip.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}
```
