Private publishing needs no certification. Certification only matters if you want a public listing on the Connect marketplace. What does gate a private Connector is the validation pipeline that runs when you request a preview or publish, and its report is the result you act on.
commercetools connect validate locally and saw critical findings. The preview request for the same tag passed. By the end of this page, you know which result to trust, and you have a privately published Connector ready to install.The Creator flow
Before you start
You need the following:
- The
v0.2.0tag from Implement and test the event app to the contract, pushed to your repository. - The CLI's API Client from Start from the email template. This page uses its
manage_connectorsscope, and the preview Deployment usesmanage_connectors_deploymentsandmanage_api_clients. - Repository access for Connect. If your repository is private, Connect needs its SSH URL and read access for the connect-mu machine user. Create an Organization Connector describes the SSH and HTTPS options.
curl and jq. Get a token once and reuse it:export CTP_PROJECT_KEY=<project-key>
export CTP_REGION=europe-west1.gcp
export CTP_CLIENT_ID=<client-id>
export CTP_CLIENT_SECRET=<client-secret>
export CONNECT_URL="https://connect.$CTP_REGION.commercetools.com"
TOKEN=$(curl -sS --fail-with-body -u "$CTP_CLIENT_ID:$CTP_CLIENT_SECRET" \
-X POST "https://auth.$CTP_REGION.commercetools.com/oauth/token" \
-d "grant_type=client_credentials" | jq -r .access_token)
Create a ConnectorStaged
With the Connect CLI
commercetools connect connectorstaged create \
--repository-url git@github.com:<your-account>/zen-order-confirmation.git \
--repository-tag v0.2.0 \
--creator-email you@example.com \
--name "Zen Electron order confirmation" \
--key zen-order-confirmation \
--integration-types email \
--supported-regions europe-west1.gcp
--key: choose a stable identifier now, because every later command uses it. This is easier than passing IDs around.--integration-types email: classifies the Connector without changing how the Connector runs.--supported-regions: set the Regions you've tested. Leave the flag out to allow every supported Region.
Check the result:
commercetools connect connectorstaged describe --key zen-order-confirmation
With the Connect API
curl -sS --fail-with-body -X POST "$CONNECT_URL/connectors/drafts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"key": "zen-order-confirmation",
"name": "Zen Electron order confirmation",
"integrationTypes": ["email"],
"creator": { "email": "you@example.com" },
"repository": {
"url": "git@github.com:<your-account>/zen-order-confirmation.git",
"tag": "v0.2.0"
},
"privateProjects": ["europe-west1.gcp:<project-key>"],
"supportedRegions": ["europe-west1.gcp"]
}' | jq '{key, version, status, isPreviewable}'
privateProjects, in the format {region}:{projectKey}, so that your Project can deploy the ConnectorStaged for preview and the published private Connector. The CLI's connectorstaged create has no flag for it, so CLI users add Projects with the Add Project to Private Connector update action. The response is a ConnectorStaged with status: "Draft" and isPreviewable: "none". Use one route, not both: the second request fails because the key already exists.What the validation pipeline checks
Trust the remote report
commercetools connect validate runs the same steps locally, but its SAST can be stricter than the remote gate, so it might report code smells that the remote preview accepts. Use the local run as an early warning. The remote report decides whether you can preview and publish.previewableReport or publishingReport, or on the Connector's Preview Report and Publish Report tabs in the Merchant Center. Check validation status describes both routes. Since 2 July 2026, the Merchant Center reports update in real time as each step runs.curl -sS --fail-with-body "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" \
| jq '{isPreviewable, status, previewable: .previewableReport.entries, publishing: .publishingReport.entries}'
Information entries, with titles such as SAST and SCA analysis check succeeded. Entry titles can change, so check each entry's type (ConnectorReportEntryType) rather than matching exact title text.Preview the ConnectorStaged
preview Deployment runs the ConnectorStaged's tag in your Project before you publish. The privateProjects list that you set when you created the ConnectorStaged decides which Projects can create it. Preview describes the Deployment type, and Deploy previewable ConnectorStaged states the Project rule.With the Connect CLI
connectorstaged preview requests previewable status, runs the validations, and creates the preview Deployment in one command. Pass a value for each configuration key in your connect.yaml. The provider call is stubbed, so the provider key and template IDs can be placeholders:CONFIG=(
--configuration "mail-sender.CTP_REGION=europe-west1.gcp"
--configuration "mail-sender.EMAIL_PROVIDER_API_KEY=stub"
--configuration "mail-sender.SENDER_EMAIL_ADDRESS=orders@example.com"
--configuration "mail-sender.CUSTOMER_REGISTRATION_TEMPLATE_ID=stub"
--configuration "mail-sender.CUSTOMER_EMAIL_TOKEN_CREATION_TEMPLATE_ID=stub"
--configuration "mail-sender.CUSTOMER_PASSWORD_TOKEN_CREATION_TEMPLATE_ID=stub"
--configuration "mail-sender.ORDER_CONFIRMATION_TEMPLATE_ID=order-confirmation"
--configuration "mail-sender.ORDER_STATE_CHANGE_TEMPLATE_ID=stub"
--configuration "mail-sender.ORDER_REFUND_TEMPLATE_ID=stub"
)
commercetools connect connectorstaged preview \
--key zen-order-confirmation \
--deployment-key zen-order-confirmation-preview \
--region europe-west1.gcp \
"${CONFIG[@]}"
{applicationName}.{key}={value}, because the keys belong to the mail-sender application. Keep the CONFIG array: the next pages reuse it.With the Connect API
version:VERSION=$(curl -sS --fail-with-body "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" | jq .version)
curl -sS --fail-with-body -X POST "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"version\": $VERSION, \"actions\": [{\"action\": \"updatePreviewable\"}]}" \
| jq .isPreviewable
isPreviewable is "pending". Monitor Preview status describes the transitions, and IsPreviewable defines the values. Poll the ConnectorStaged until isPreviewable isn't "pending", then read the result:until [ "$(curl -sS --fail-with-body "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" | jq -r .isPreviewable)" != "pending" ]; do
sleep 30
done
curl -sS --fail-with-body "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" \
| jq '{isPreviewable, report: .previewableReport.entries}'
isPreviewable is "true". If it's "false", read the entries with type: "Error" and fix the cause before you request a new preview.staged: true and type: preview. Define the configuration values once as a JSON array. The install and operate pages reuse it, in the same way that the CLI examples reuse CONFIG:CONFIGURATIONS='[
{
"applicationName": "mail-sender",
"standardConfiguration": [
{ "key": "CTP_REGION", "value": "europe-west1.gcp" }
],
"securedConfiguration": [
{ "key": "EMAIL_PROVIDER_API_KEY", "value": "stub" },
{ "key": "SENDER_EMAIL_ADDRESS", "value": "orders@example.com" },
{ "key": "ORDER_CONFIRMATION_TEMPLATE_ID", "value": "order-confirmation" },
{ "key": "CUSTOMER_REGISTRATION_TEMPLATE_ID", "value": "stub" },
{ "key": "CUSTOMER_EMAIL_TOKEN_CREATION_TEMPLATE_ID", "value": "stub" },
{ "key": "CUSTOMER_PASSWORD_TOKEN_CREATION_TEMPLATE_ID", "value": "stub" },
{ "key": "ORDER_STATE_CHANGE_TEMPLATE_ID", "value": "stub" },
{ "key": "ORDER_REFUND_TEMPLATE_ID", "value": "stub" }
]
}
]'
jq -n --argjson configurations "$CONFIGURATIONS" '{
key: "zen-order-confirmation-preview",
type: "preview",
connector: { key: "zen-order-confirmation", staged: true },
region: "europe-west1.gcp",
configurations: $configurations
}' | curl -sS --fail-with-body -X POST "$CONNECT_URL/$CTP_PROJECT_KEY/deployments" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @- | jq '{key, type, status}'
CONFIGURATIONS variable: the next pages reuse it.preview. Environments explains which types need a published Connector.Test the preview, then delete it
commercetools connect deployment delete --key zen-order-confirmation-preview
y/n confirmation prompt, which can look like a hang in a script.Every new tag needs a new preview request. A preview Deployment keeps running the tag it was created from.
Publish privately
certification: false to create a private Connector:commercetools connect connectorstaged publish --key zen-order-confirmation
VERSION=$(curl -sS --fail-with-body "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" | jq .version)
curl -sS --fail-with-body -X POST "$CONNECT_URL/connectors/drafts/key=zen-order-confirmation" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"version\": $VERSION, \"actions\": [{\"action\": \"publish\", \"certification\": false}]}" \
| jq '{status, isPreviewable}'
status until it isn't Processing (ConnectorStatus defines the values), then read publishingReport for the result. To wait for it in a script, reuse the polling loop from the preview step with .status and Processing.privateProjects list. The reliable approach is to list every intended Project, including the creator Project, either in the create request or with the Add Project to Private Connector update action before installation. Project identifiers use {region}:{projectKey}, for example europe-west1.gcp:zen-electron-dev.privateProjects before you install there.Deleting a ConnectorStaged
Certification, briefly
Production-readiness checklist
commercetools-connect skill's production-readiness checklist covers that gap and gives the full criteria and implementation patterns. The following items are recommended practice, not platform requirements, and are the ones this Connector needs most:- Delivery policy: the README records the acknowledgment policy and deduplication for each email type. Implement and test the event app to the contract did this.
- Message-type and self-change filtering: check the Connector against the skill's Correctness items.
- Least-privilege scopes and secured secrets: check
inheritAs.apiClient.scopesandsecuredConfigurationagainst the skill's Security items. - Structured logs with correlation keys: log lines carry no Customer data.
- Repeatable lifecycle scripts: verify the resource after deployment instead of assuming it from the Deployment status.
- A runbook in the README: cover what happens when the provider is down and how an operator finds missed sends.
Worked example: publish Zen Electron's Connector
- Create the ConnectorStaged
zen-order-confirmationfrom tagv0.2.0. - Run
connectorstaged previewwith theCONFIGarray. The preview report passes. - Check the preview Deployment as described on the next page, then delete it.
- Publish with
certification: falseand confirmstatus: "Published".
privateProjects note in Publish privately first.Key takeaways
- A ConnectorStaged is the editable draft that references a repository tag. Publishing it creates the read-only Connector.
- Every preview and publish request runs the validation pipeline. Automated tests run only for certification.
- The remote report is the gate. Local
connect validatecan be stricter, so use it as an early warning. - A ConnectorStaged can be deployed only as
preview. Delete preview Deployments after testing, and request a new preview for each new tag. - Publishing with
certification: falsecreates a private Connector without certification. Certification is only for public listings. - Deleting a ConnectorStaged also deletes its published Connector, and the CLI can't do it.