Project Duplicator API

Create a new project from an existing one: its settings, its members and its teams. No content is copied — no files, strings, translations, TM or glossary entries, whatever the flags say: they are named after settings so that none of them reads as a copy of content.

Contract version 1. Call it through the Crowdin API with a token that has the application.proxy scope:

Parameters go in the JSON body or in the query; Crowdin merges the query into the body, so numbers and booleans are accepted as text too. Crowdin wraps every answer into data and keeps its HTTP status, so every body says ok and status itself.

How a copy goes

  1. GET /duplication-preview shows what would be copied, who would be added, invited or skipped and why, and what is never copied. It writes nothing, and it is also the export of who has access to the source.
  2. POST /duplication with an idempotencyKey answers once the project is created, with the status of the copy. Members, teams and resources are copied after the answer.
  3. GET /duplication-status every pollAfterMs (5000 ms), or with wait, until the status is one of succeeded, partial, failed, needsAttention.

Statuses: queued, running, succeeded, partial, failed, needsAttention, interrupted. interrupted is not final: the app is restarting, and the copy goes on once it is back, so keep polling. partial means the project exists but something did not make it: show it as created with remarks, never as a success. needsAttention means Crowdin did not confirm the creation and the app will not guess: candidates lists the projects it may have made, and a POST with the same key and adoptProjectId goes on with one of them. Phases, in progress.phase only: creating, settings, members, teams, resources, done.

Endpoints

GET /duplication-preview

Show what would be copied from a source project, which members would be added with their roles, who would be skipped, and what this app never copies. No content is copied — no files, strings, translations, TM or glossary entries. On crowdin.com adding a translator, or any other member, sends them an invitation email, so the preview counts the emails first.

Path through Crowdin: GET …/api/duplication-preview

ParameterTypeRequiredPlatformDescription
sourceProjectIdintegeryesbothThe project to copy.
targetGroupIdintegernoEnterpriseThe group the copy would be created in; 0 or none for the root.
workflowTemplateIdintegernoEnterpriseThe workflow template of the copy. Without it the preview maps the steps onto the template whose steps match the source and names it in target.suggestedWorkflowTemplateId.
copyobjectnobothThe copy flags below, each a boolean. A flag left out takes its default; teams follows members unless it is given. Any other key is refused.
memberOptionsobjectnoboth{ onUnmappedSteps: "skipRole" | "strict" | "allContent" (default "skipRole"), acknowledgeInvitations: boolean }. See "Members".

Response 200

{
  "ok": true,
  "status": "ok",
  "platform": "enterprise",
  "source": {
    "id": 123,
    "name": "Mobile App",
    "identifier": "mobile-app",
    "type": "file-based",
    "sourceLanguageId": "en",
    "sourceLanguageName": "English",
    "targetLanguages": [
      {
        "id": "uk",
        "name": "Ukrainian"
      }
    ],
    "hasAdvancedWorkflow": true,
    "workflowSteps": [
      {
        "id": 313,
        "type": "Translate",
        "title": "Translation"
      }
    ],
    "settingsValues": {
      "qaCheckIsActive": true
    }
  },
  "target": {
    "groupId": 45,
    "groupPath": "Workspace/Mobile",
    "workflowTemplateId": 7,
    "suggestedWorkflowTemplateId": null,
    "workflowTemplateRequired": true,
    "stepMapping": [
      {
        "sourceStepId": 313,
        "targetStepId": 998,
        "matchedBy": "positional"
      }
    ],
    "mappingMethod": "positional"
  },
  "settings": {
    "groups": [
      {
        "key": "qa",
        "title": "Quality assurance",
        "default": true,
        "resource": false,
        "fields": [
          "qaCheckIsActive",
          "qaCheckCategories"
        ],
        "values": {
          "qaCheckIsActive": true
        },
        "requires": [],
        "blockedBy": null
      },
      {
        "key": "placeholders",
        "title": "Placeholders",
        "default": true,
        "resource": true,
        "fields": [],
        "values": {
          "switchedOff": [
            {
              "id": "printfSpecifier",
              "label": "C-style format specifiers"
            }
          ],
          "customPlaceholders": [
            {
              "name": "Ticket reference",
              "customPlaceholderId": 12,
              "type": "high",
              "index": 0,
              "isBlocking": true,
              "formats": [
                "docx"
              ]
            }
          ]
        },
        "requires": [],
        "blockedBy": null
      }
    ]
  },
  "members": {
    "copied": true,
    "total": 42,
    "byOutcome": {
      "willAdd": 33,
      "willInvite": 0,
      "willSkip": 9
    },
    "estimatedInvitations": 0,
    "list": [
      {
        "id": 11,
        "login": "ann",
        "name": "Ann",
        "roles": [
          "translator"
        ],
        "languages": [
          "uk"
        ],
        "allLanguages": false
      }
    ],
    "skipped": [
      {
        "id": 12,
        "login": "eve",
        "name": "Eve",
        "code": "MEMBER_BLOCKED_SKIPPED",
        "message": "This member is blocked in the source project, so they were not added."
      }
    ],
    "detailsTruncated": false
  },
  "teams": {
    "copied": true,
    "supported": true,
    "total": 3,
    "willAdd": 3,
    "list": [
      {
        "id": 4,
        "name": "Reviewers"
      }
    ],
    "skipped": []
  },
  "resources": {
    "labels": 12,
    "exporterSettings": 3,
    "taskSettingsTemplates": 1,
    "reportSettingsTemplates": null,
    "resourceBindings": 2,
    "placeholders": 2
  },
  "warnings": [],
  "notCopied": [
    {
      "key": "content.files",
      "title": "Files and folders",
      "reason": "outOfScope",
      "code": null
    }
  ],
  "unknownFields": [],
  "limits": {
    "maxMembers": 5000,
    "maxTeams": 500,
    "memberBatchSize": 25,
    "maxWaitSeconds": 60,
    "invitationConfirmationThreshold": 50
  },
  "contract": {
    "version": 1,
    "duplicationUrl": "/duplication",
    "statusUrl": "/duplication-status",
    "idempotencyKeyRequired": true,
    "pollAfterMs": 5000
  }
}

POST /duplication

Create a new project from an existing one: settings, members with their roles and, on Enterprise, teams. No content is copied — no files, strings, translations, TM or glossary entries. On crowdin.com adding a translator, or any other member, sends them an invitation email.

Path through Crowdin: POST …/api/duplication

ParameterTypeRequiredPlatformDescription
idempotencyKeystringyesbothUp to 128 characters, one per copy. The same key with the same request answers with the same duplication and never creates a second project; the same key with another request is IDEMPOTENCY_CONFLICT. A copy that failed with UPSTREAM_ERROR or UPSTREAM_RATE_LIMITED before its project existed is tried again by the same request with the same key, which first looks for a project Crowdin may have created after all; any other failure answers the same again.
sourceProjectIdintegeryesbothThe project to copy.
namestringyesbothThe name of the new project.
identifierstringnocrowdin.comThe identifier of the new project: letters, digits, "-" and "_", at least 3. Without it the app derives one from the name and adds -2, -3 when it is taken; one given here is never replaced, and a taken one is IDENTIFIER_TAKEN. On Enterprise it is ignored with FIELD_IGNORED_ON_PLATFORM.
targetGroupIdintegernoEnterpriseThe group to create the copy in; 0 or none for the root.
workflowTemplateIdintegernoEnterpriseThe workflow template of the copy. Required when the preview says target.workflowTemplateRequired: the source has a workflow of its own, and Crowdin does not remember which template it was made from. A workflow Crowdin does not answer with is UPSTREAM_ERROR rather than a guess, on the preview as on the POST.
adoptProjectIdintegernobothOnly to resolve a duplication in needsAttention, sent again with its idempotencyKey: the candidate project the copy goes on with, as the duplication planned it, whatever else the request says. Anything but one of its candidates is ADOPT_CANDIDATE_MISMATCH; the candidate it already went on with is answered as a replay.
copyobjectnobothThe copy flags below, each a boolean. A flag left out takes its default; teams follows members unless it is given. Any other key is refused.
memberOptionsobjectnoboth{ onUnmappedSteps: "skipRole" | "strict" | "allContent" (default "skipRole"), acknowledgeInvitations: boolean }. See "Members".
waitnumbernoboth0..60 seconds to wait for the copy to finish before answering. A long poll instead of a frequent one; an interrupted copy is answered at once.

Request

{
  "idempotencyKey": "7f1c2a0e-3c1b-4d0e-9a57-5a4c0f1d2e3b",
  "sourceProjectId": 123,
  "name": "Mobile App (copy)",
  "targetGroupId": 45,
  "workflowTemplateId": 7,
  "copy": {
    "targetLanguages": true,
    "qa": true,
    "placeholders": true,
    "sourceStringSettings": true,
    "importSettings": true,
    "exportSettings": true,
    "translationSettings": true,
    "tmSettings": true,
    "aiPrompts": true,
    "notifications": true,
    "languageMapping": true,
    "inContext": false,
    "labels": false,
    "exporterSettings": false,
    "taskSettingsTemplates": false,
    "reportSettingsTemplates": false,
    "resourceBindings": false,
    "members": true,
    "teams": true
  },
  "memberOptions": {
    "onUnmappedSteps": "skipRole",
    "acknowledgeInvitations": false
  },
  "wait": 0
}

Response 200

{
  "ok": true,
  "status": "running",
  "duplicationId": "dup_01J8ZB6Q4W2N5V7X9Y0A1B2C3D",
  "idempotencyKey": "7f1c2a0e-3c1b-4d0e-9a57-5a4c0f1d2e3b",
  "project": {
    "id": 987,
    "name": "Mobile App (copy)",
    "identifier": null,
    "url": "https://acme.crowdin.com/u/projects/987/files"
  },
  "candidates": [],
  "startedAt": "2026-09-23T10:00:00.000Z",
  "finishedAt": null,
  "progress": {
    "phase": "members",
    "done": 12,
    "total": 45
  },
  "report": {
    "settings": {
      "requested": [
        "targetLanguages",
        "qa",
        "aiPrompts"
      ],
      "copied": [
        "targetLanguages",
        "qa"
      ],
      "failed": [
        {
          "key": "aiPrompts",
          "code": "SETTING_REJECTED",
          "message": "qaCheckActionAiPromptId: <the wording of Crowdin>",
          "details": {
            "fields": [
              "qaCheckActionAiPromptId"
            ],
            "retryable": true
          }
        }
      ]
    },
    "members": {
      "total": 42,
      "counts": {
        "added": 33,
        "updated": 0,
        "invited": 0,
        "skipped": 8,
        "failed": 1
      },
      "added": [
        {
          "id": 11,
          "login": "ann",
          "roles": [
            "translator"
          ],
          "languages": [
            "uk"
          ],
          "allLanguages": false
        }
      ],
      "skipped": [],
      "failed": [],
      "detailsTruncated": false
    },
    "teams": {
      "supported": true,
      "total": 3,
      "counts": {
        "added": 2,
        "skipped": 1,
        "failed": 0
      },
      "added": [
        {
          "id": 4,
          "name": "Reviewers"
        }
      ],
      "skipped": [
        {
          "id": 9,
          "name": "FR squad",
          "code": "TEAM_PERMISSIONS_NOT_PORTABLE",
          "message": "The access of this team cannot be rebuilt in the new project, so the team was not added."
        }
      ],
      "failed": []
    },
    "resources": {
      "labels": {
        "total": 12,
        "copied": 12,
        "failed": 0,
        "skipped": 0,
        "items": []
      },
      "placeholders": {
        "total": 2,
        "copied": 1,
        "failed": 0,
        "skipped": 1,
        "items": [
          {
            "name": "Ticket reference",
            "outcome": "skipped",
            "code": "CUSTOM_PLACEHOLDER_FORMAT_UNAVAILABLE",
            "message": "This custom placeholder is limited to file formats the new project cannot use, such as one of an app the project has no access to, so it was not assigned. Give the project access to that format, then assign the placeholder by hand."
          }
        ]
      }
    },
    "notCopied": [
      {
        "key": "content.files",
        "title": "Files and folders",
        "reason": "outOfScope",
        "code": null
      },
      {
        "key": "webhooks",
        "title": "Webhooks",
        "reason": "outOfScope",
        "code": "WEBHOOKS_NOT_COPIED"
      }
    ],
    "warnings": [],
    "unknownFields": []
  },
  "errors": [],
  "statusUrl": "/duplication-status",
  "pollAfterMs": 5000,
  "replayed": false
}

GET /duplication-status

Report of a duplication: which settings were copied, which members were added with their roles or, on crowdin.com, invited by email, who was skipped, and who failed with the reason. No content is copied.

Path through Crowdin: GET …/api/duplication-status

ParameterTypeRequiredPlatformDescription
duplicationIdstringnobothThe duplicationId the POST answered with.
idempotencyKeystringnobothOr the key the POST was sent with. One of the two is required.
waitnumbernoboth0..60 seconds to wait for the copy to finish before answering. A long poll instead of a frequent one; an interrupted copy is answered at once.

Response 200

{
  "ok": true,
  "status": "partial",
  "duplicationId": "dup_01J8ZB6Q4W2N5V7X9Y0A1B2C3D",
  "idempotencyKey": "7f1c2a0e-3c1b-4d0e-9a57-5a4c0f1d2e3b",
  "project": {
    "id": 987,
    "name": "Mobile App (copy)",
    "identifier": null,
    "url": "https://acme.crowdin.com/u/projects/987/files"
  },
  "candidates": [],
  "startedAt": "2026-09-23T10:00:00.000Z",
  "finishedAt": "2026-09-23T10:01:12.000Z",
  "progress": {
    "phase": "done",
    "done": 45,
    "total": 45
  },
  "report": {
    "settings": {
      "requested": [
        "targetLanguages",
        "qa",
        "aiPrompts"
      ],
      "copied": [
        "targetLanguages",
        "qa"
      ],
      "failed": [
        {
          "key": "aiPrompts",
          "code": "SETTING_REJECTED",
          "message": "qaCheckActionAiPromptId: <the wording of Crowdin>",
          "details": {
            "fields": [
              "qaCheckActionAiPromptId"
            ],
            "retryable": true
          }
        }
      ]
    },
    "members": {
      "total": 42,
      "counts": {
        "added": 33,
        "updated": 0,
        "invited": 0,
        "skipped": 8,
        "failed": 1
      },
      "added": [
        {
          "id": 11,
          "login": "ann",
          "roles": [
            "translator"
          ],
          "languages": [
            "uk"
          ],
          "allLanguages": false
        }
      ],
      "skipped": [],
      "failed": [],
      "detailsTruncated": false
    },
    "teams": {
      "supported": true,
      "total": 3,
      "counts": {
        "added": 2,
        "skipped": 1,
        "failed": 0
      },
      "added": [
        {
          "id": 4,
          "name": "Reviewers"
        }
      ],
      "skipped": [
        {
          "id": 9,
          "name": "FR squad",
          "code": "TEAM_PERMISSIONS_NOT_PORTABLE",
          "message": "The access of this team cannot be rebuilt in the new project, so the team was not added."
        }
      ],
      "failed": []
    },
    "resources": {
      "labels": {
        "total": 12,
        "copied": 12,
        "failed": 0,
        "skipped": 0,
        "items": []
      },
      "placeholders": {
        "total": 2,
        "copied": 1,
        "failed": 0,
        "skipped": 1,
        "items": [
          {
            "name": "Ticket reference",
            "outcome": "skipped",
            "code": "CUSTOM_PLACEHOLDER_FORMAT_UNAVAILABLE",
            "message": "This custom placeholder is limited to file formats the new project cannot use, such as one of an app the project has no access to, so it was not assigned. Give the project access to that format, then assign the placeholder by hand."
          }
        ]
      }
    },
    "notCopied": [
      {
        "key": "content.files",
        "title": "Files and folders",
        "reason": "outOfScope",
        "code": null
      },
      {
        "key": "webhooks",
        "title": "Webhooks",
        "reason": "outOfScope",
        "code": "WEBHOOKS_NOT_COPIED"
      }
    ],
    "warnings": [],
    "unknownFields": []
  },
  "errors": [],
  "statusUrl": "/duplication-status",
  "pollAfterMs": 5000
}

Errors

The same envelope on all three; error.httpStatus is the HTTP status of the answer, and error.duplicationId names the duplication a refused copy ended in.

{
  "ok": false,
  "status": "error",
  "error": {
    "code": "SOURCE_FORBIDDEN",
    "message": "You need manager access to the source project to copy its settings and members.",
    "httpStatus": 403,
    "details": {
      "projectId": 123
    },
    "duplicationId": null
  }
}

What is copied

Always, whatever the flags: sourceLanguageId, type, description, visibility (crowdin.com), fields (Enterprise). The flags:

FlagDefaultWhat it copiesRequires
targetLanguages
Target languages
truetargetLanguageIds—
qa
Quality assurance
trueqaCheckIsActive, qaCheckCategories, qaChecksIgnorableCategories, qaApprovalsCount (Enterprise), customQaCheckIds (Enterprise), externalQaCheckIds (Enterprise), externalQaChecksIgnorable (Enterprise)—
placeholders
Placeholders
trueWhich built-in placeholders are recognized and, on Enterprise, the custom ones assigned to the project.—
sourceStringSettings
Source string settings
truenormalizePlaceholder, saveMetaInfoInSource (file-based only)—
importSettings
Import
truenormalizePlaceholder, tagsDetection—
exportSettings
Export
trueskipUntranslatedStrings, skipUntranslatedFiles (file-based only), autoTranslateDialects, exportApprovedOnly (crowdin.com), exportWithMinApprovalsCount (Enterprise), exportStringsThatPassedWorkflow (Enterprise)—
translationSettings
Translation settings
truelanguageAccessPolicy (crowdin.com), delayedWorkflowStart (Enterprise), translateDuplicates (file-based only), isMtAllowed, publicDownloads, glossaryAccess, glossaryAccessOption, taskBasedAccessControl, taskReviewerIds (Enterprise), hiddenStringsProofreadersAccess, mtPreTranslate (crowdin.com)—
tmSettings
Translation Memory
trueautoSubstitution, tmContextType (file-based only), showTmSuggestionsDialects, tmApprovedSuggestionsOnly, useGlobalTm (crowdin.com), tmPreTranslate (crowdin.com), tmPenalties—
aiPrompts
AI Default Prompts
trueqaCheckActionAiPromptId, editorSuggestionAiPromptId, preTranslationAiPromptId, contextReviewAiPromptId, alignmentActionAiPromptId (Enterprise), aiPreTranslate (crowdin.com)—
notifications
Notifications
truenotificationSettings—
languageMapping
Language Mapping
truelanguageMapping—
inContext
In-Context
falseinContext, inContextProcessHiddenStrings, inContextPseudoLanguageId—
labels
Labels
falseThe project's own labels, without the strings they are assigned to.—
exporterSettings
File format and strings exporter settings
falseParser options per file format with their custom segmentation rules, and the XLIFF, Android and iOS export options.—
taskSettingsTemplates
Task settings templates
falseSaved templates, not tasks; they need Target languages, and only assignees who joined the copy can stay.—
reportSettingsTemplates
Report settings templates
falseThe project's own saved templates, not the account-wide ones every project already sees. Only on crowdin.com.—
resourceBindings
Translation memories, glossaries, style guides and the savings report template
falseassignedStyleGuides, assignedTms, assignedGlossaries, savingsReportSettingsTemplateId—
members
Members
trueThe members of the project with their roles, languages and workflow steps.targetLanguages
teams
Teams
as membersThe teams of the project with their roles. Only on Enterprise.targetLanguages

A setting Crowdin refuses on creation is left out and the project is created without it, reported in report.settings.failed with the wording of Crowdin and a partial status. Resource bindings attach the TMs, glossaries and style guides of the source, its default TM and glossary among them, to the copy as additional ones; the copy keeps the TM and the glossary Crowdin creates with it as its defaults, so its translations never go into a TM of the source. Their entries are not copied. TM priorities stay those of the source on crowdin.com, where the highest one is the number of TMs of the project owner. Enterprise accepts no priority above the number of TMs of the project, so there priorities the copy cannot have become their rank, in the same order. The preview counts what each resource flag would copy under resources, one key per flag; a count is null when the app does not copy that group here (settings.groups[].blockedBy says why) or could not count it.

placeholders, on by default, copies the placeholders the project recognizes, which Crowdin keeps apart from the settings of the project: every built-in placeholder switched off in the source is switched off in the copy, one JSON-Patch each, and on Enterprise the custom placeholders of the organization assigned to the source are assigned to the copy with the same type, index, isBlocking and formats, in the order of their index. The custom placeholders themselves belong to the organization and are shared, so none is created or changed. One limited to formats the copy cannot use is skipped with CUSTOM_PLACEHOLDER_FORMAT_UNAVAILABLE, and one deleted meanwhile with CUSTOM_PLACEHOLDER_DELETED; neither makes the copy partial. Placeholder unification, normalizePlaceholder, is a setting of sourceStringSettings and importSettings. In the preview, settings.groups[] of placeholders lists the built-in placeholders switched off in the source under values.switchedOff and, on Enterprise, its custom placeholders under values.customPlaceholders, null where Crowdin did not list them. Where Crowdin has no placeholder routes (Crowdin Enterprise of the Connector edition answers 404), the group is reported in notCopied with endpointUnavailable, which is also its blockedBy in the preview, it is not counted, and the copy is not partial for it. A caller that leaves the flag out gets placeholders copied.

Resources are copied last, after the teams and the members. Labels go by title, without the strings they are assigned to; system labels are left alone. Task settings templates go with their languages as they are and keep as assignees only the people and teams the copy has; a template without languages, and every template when the target languages are not copied, is skipped with LANGUAGE_NOT_IN_TARGET. The report counts every resource flag under report.resources (total, copied, failed, skipped), and items names what failed or left something behind. A resource list Crowdin refuses to give makes the copy partial, never stops it.

Members

Members are added with the roles they have in the source, rebuilt for the languages and workflow steps of the copy. memberOptions.onUnmappedSteps decides what happens to access to a step the copy has no match for: skipRole (default) leaves that access out, strict refuses the copy before anything is created, allContent widens it to all steps and names every widened grant in the report. The owner of the source, blocked members, pending invitations (crowdin.com), organization admins and group managers without a project role of their own, and technical accounts are never added, each with its code; an admin or a group manager with a role of their own gets that role alone. On Enterprise a person who has not accepted the invitation to the organization yet is copied like any member. On Enterprise the teams of the source are added after the members, since Crowdin skips whoever a team already makes a manager of the copy with their roles of their own, and the people of a team are not added one by one.

Access can be limited to Translate and Proofread steps only, so only those steps of the source and of the workflow template are compared; pre-translation, vendor, application and custom code steps neither count nor shift the positions. They map by position when both have them in the same order of types, by position within a type when only the order or the titles differ, and not at all otherwise: target.mappingMethod is positional, byType or none, and target.stepMapping pairs each such step of the source with its step in the copy, null when it has none. A member or a team left without a role because its languages are not target languages of the copy is skipped with LANGUAGE_NOT_IN_TARGET under every policy. When the preview counted on those languages and the creation of the copy lost them, the member or the team fails with the same code instead.

With copy.members or copy.teams off, the preview keeps members and teams in their shape with copied: false and total: null: nobody was read, so there is nothing to count. teams.supported is then null on Enterprise, since whether Crowdin lists the teams of the source is only known once they are asked for.

On Enterprise the task reviewers of translationSettings are set once the members and the teams are in, and only those who are managers of the copy then: Crowdin accepts no one else. The others are listed in notCopied under taskReviewerIds with details.reason notManagers and their details.userIds; a refusal of Crowdin is a failed setting of a partial copy.

Never copied

WhatWhy
content.filesFiles and folders
content.branchesBranches
content.stringsSource strings
content.translationsTranslations and approvals
content.translationMemoryTranslation memory entries
content.glossaryGlossary terms
content.screenshotsScreenshots
content.commentsComments and issues
content.tasksTasks
content.distributionsDistributions and bundles
content.labelAssignmentsLabels assigned to strings
webhooksWebhooks are not copied: Crowdin does not give back their authorization headers, and a copy would call the same integrations a second time. Set them up in the new project by hand.
idAssigned by Crowdin to the new project.
identifiercrowdin.com derives it from the new name and needs it unique; Enterprise does not accept it on create.
nameThe name of the copy comes from the request.
userIdThe owner of the copy is whoever runs the duplication.
createdAtSet by Crowdin.
updatedAtSet by Crowdin.
lastActivitySet by Crowdin.
logoProject creation does not accept a logo.
webUrlBelongs to the source project.
isSuspendedState of the organization, not a project setting.
exportTranslatedOnlyDeprecated duplicate of skipUntranslatedStrings, which is copied.
sourceLanguageExpanded object; sourceLanguageId is copied.
targetLanguagesExpanded objects; targetLanguageIds is copied.
inContextPseudoLanguageExpanded object; inContextPseudoLanguageId is copied.
defaultTmIdThe copy keeps the TM Crowdin creates with it as its default, so its translations never go into a TM of the source; resourceBindings attaches the default TM of the source as an additional one.
defaultGlossaryIdThe copy keeps the glossary Crowdin creates with it as its default; resourceBindings attaches the default glossary of the source as an additional one.
translateDuplicatesFixed by Crowdin for string-based projects and not accepted on their creation.
cnameA unique domain: a copy would either take it from the source or fail on the conflict.
groupIdThe target group comes from the request.
backgroundDeprecated and not accepted on create.
publicUrlBelongs to the source project.
workflowIdThe source project's own workflow, not a template: the workflow of the copy comes from the chosen template.
hasCrowdsourcingDerived by Crowdin from the workflow.
isExternalVendor relationship of the source project.
externalTypeVendor relationship of the source project.
externalProjectIdVendor relationship of the source project.
externalOrganizationIdVendor relationship of the source project.
clientOrganizationIdVendor relationship of the source project.

notCopied[].reason is one of outOfScope, notImplemented, platformUnsupported, crowdinOnly, endpointUnavailable.

Codes

HTTP errors

CodeHTTPMessage
VALIDATION_FAILED400A parameter of the request is missing or not valid: error.details.field names it.
SETTING_REJECTED400Crowdin rejected some of the copied settings.
INVALID_PROJECT_ID400The source project id must be a positive integer.
IDEMPOTENCY_KEY_REQUIRED400Every duplication request needs an idempotency key of at most 128 characters, so that a retry does not make a second copy.
TARGET_LANGUAGES_REQUIRED_FOR_MEMBERS400Members and teams can only be copied together with the target languages. Turn on the target languages or turn off the members and teams.
IDENTIFIER_TAKEN409Another project already uses this identifier. Choose a different one.
IDEMPOTENCY_CONFLICT409This idempotency key was already used for a different duplication request. Use a new key for a new request.
ADOPT_CANDIDATE_MISMATCH400The project to adopt is not one of the candidates found for this duplication. Choose one of its candidates.
SOURCE_TOO_LARGE400The source project has more members or teams than this app copies at once.
WORKFLOW_TEMPLATE_REQUIRED400The source project has its own workflow, so the copy needs a workflow template. Choose one and start again.
WORKFLOW_STEPS_NOT_MAPPABLE400Some workflow steps members of the source project have access to have no match in the chosen template, so nothing was created.
INVITATIONS_NOT_ACKNOWLEDGED400Copying these members sends many invitation emails on crowdin.com. Confirm the invitations and start again.
INVALID_TOKEN401The request is not signed with a valid Crowdin token. Call this API through the Crowdin API of your organization.
MISSING_TOKEN401The request carries no Crowdin token. Call this API through the Crowdin API of your organization.
TOKEN_EXPIRED401The Crowdin token of the request has expired. Send the request again.
SOURCE_FORBIDDEN403You need manager access to the source project to copy its settings and members.
TARGET_GROUP_NOT_FOUND404The target group does not exist in this organization.
APP_NOT_INSTALLED404The app is installed, but the organization is unknown to it. Reinstall the app.
SOURCE_NOT_FOUND404The source project does not exist, or the app cannot see it.
DUPLICATION_NOT_FOUND404There is no such duplication.
UPSTREAM_RATE_LIMITED429Crowdin is limiting how often this app may call it right now. Try again in a few minutes.
INTERNAL_ERROR500Something went wrong in the app. Try again later, with a new idempotencyKey for a copy that ended with this error.
UPSTREAM_ERROR502Crowdin did not answer as expected. Try again in a few minutes.
APP_RESTARTING503The app is restarting. Send the same request with the same idempotencyKey again in a minute: it never makes a second copy.

Two of them came after version 1 of the contract froze its catalog:

In the report

Per setting, member, team and warning, inside a copy that as a whole went through.

CodeMeaning
SETTING_REJECTEDCrowdin rejected this setting, so the new project was created without it.
FIELD_IGNORED_ON_PLATFORMThis value is not accepted on this platform, so it was not copied.
WORKFLOW_STEP_CONFIG_SKIPPEDThe vendor or MT engine of these workflow steps was not copied: the new project takes its workflow from the chosen template.
ROLE_NOT_COPYABLEThis role cannot be given in the new project, so the member was not added.
MEMBER_ALREADY_PRESENTAlready in the new project.
MEMBER_BLOCKED_SKIPPEDThis member is blocked in the source project, so they were not added.
MEMBER_PENDING_SKIPPEDThis person has not accepted the invitation to the source project yet. Pending invitations are not copied, and no second email is sent.
INHERITED_ACCESS_NOT_PORTABLEThis member reaches the source project through the organization, a group or a team, not through a project role of their own. Adding them in person would grant access they never had, so they were not added.
TEAM_ONLY_ACCESSThis member reaches the source project only through a copied team, so the team gives them access to the new project.
TECHNICAL_DETECTION_UNAVAILABLECrowdin does not mark technical accounts among the members of a project: one is found when Crowdin refuses it.
LANGUAGE_NOT_IN_TARGETThis language is not a target language of the new project, so access to it was not copied.
WORKFLOW_STEP_UNMAPPEDSome workflow steps of the source project have no match in the new project. The details say whose access was left out or widened.
STEP_RESTRICTIONS_NOT_PORTABLEThe new project has the default workflow, so these members get access to all workflow steps of their languages.
TEAM_PERMISSIONS_NOT_PORTABLEThe access of this team cannot be rebuilt in the new project, so the team was not added.
INVITE_LIMIT_REACHEDCrowdin reached the daily limit of invitation emails of the organization, so this member was not invited.
MANAGERS_LIMIT_EXCEEDEDThe subscription plan of the organization allows no more managers, so this member was not added.
UPSTREAM_RESULT_UNKNOWNCrowdin did not answer whether this member or team was added, and the new project did not show it when the app checked. Check its members before you add them by hand: Crowdin may still add them, and on crowdin.com the invitation may have been sent.
CUSTOM_PLACEHOLDER_FORMAT_UNAVAILABLEThis custom placeholder is limited to file formats the new project cannot use, such as one of an app the project has no access to, so it was not assigned. Give the project access to that format, then assign the placeholder by hand.
CUSTOM_PLACEHOLDER_DELETEDThis custom placeholder was deleted from the organization while the copy was made, so there was nothing to assign.
WEBHOOKS_NOT_COPIEDWebhooks are not copied: Crowdin does not give back their authorization headers, and a copy would call the same integrations a second time. Set them up in the new project by hand.
NEEDS_ATTENTIONThe duplication stopped at a point where the app cannot decide on its own. Check the details to resolve it.

Three of them came after version 1 of the contract froze its catalog:

Errors that never reach the app

Answer of CrowdinMeaning
401 api.notAllowedScopeThe token of the caller lacks the application.proxy scope, which came with API version 2025-05-13: older personal access tokens and OAuth apps do not have it. It is not INVALID_TOKEN of the app.
404 on all three pathsThe app is not installed, or an admin has not applied the update that added these endpoints.
404 on crowdin.comFor everyone but the account the app is installed under, for good.
405The path is right, the method is not.
500 without a bodyThe 15-minute timeout of the proxy. A retry with the same idempotencyKey is safe.

Limits

Limitations

  1. Who can call it: on Enterprise the owner and the admins of the organization, on crowdin.com only the account the app is installed under. Project managers duplicate projects in the window of the app. A 403 or a 404 from Crowdin for anyone else means exactly this.
  2. Crowdin does not check the permission of an api module on its proxy, only whether the caller has full access to the apps of the organization: whoever can call one of the three endpoints can call all of them.
  3. The app acts with the permissions of whoever installed it, never with those of the caller: there is no token per user. A preview and a duplication check the caller in the app as well, for a call that comes past the proxy: on Enterprise an admin of the organization, on crowdin.com a manager of the source project. The status of a run is found by its id, 80 random bits. Enterprise has an app authentication of the type crowdin_app_with_code, which acts as the caller, and crowdin.com has none; this app does not use it and acts with the token of the installer on both.
  4. Crowdin waits 15 minutes for an answer and has no callbacks, so the API is asynchronous: a POST answers once the project is created, and wait is capped at 60 seconds.
  5. The paths of api modules have no dynamic segments: every id is a parameter.
  6. On crowdin.com a copy always gets the default workflow, as project creation there accepts no workflow, so access the source limits to some workflow steps is lost in the copy: those members get all the steps of their languages. This cannot be detected through the API: for a source with a workflow of its own Crowdin already reports such access as all the steps of the language, so the preview and the report do not name these members. STEP_RESTRICTIONS_NOT_PORTABLE appears only for the access Crowdin does report as limited to some steps.
  7. On crowdin.com every member added gets an email against the daily limit of invitations of the organization: 1000 a day outside a trial, 2 to 100 a day in a trial depending on its age, and a separate limit of 50 for invitations with the manager role (numbers of Crowdin that may change). A manager or a developer joins the copy at once; a translator, a proofreader or a language coordinator is invited and joins once they accept. The rest of the limit cannot be read; above 50 estimated invitations the POST needs memberOptions.acknowledgeInvitations, and once Crowdin refuses one the remaining ones are reported as INVITE_LIMIT_REACHED.
  8. On crowdin.com a member is a manager, a developer or holds language roles, never two of these: a member the source lists as a manager or a developer with language roles as well is added with the manager or the developer role alone. A manager has every language with the role; for a developer the preview and the report name the language roles left out in notCopyableRoles.
  9. Rolling the app back removes its api modules from every organization by itself; bringing them back takes another Apply of the update by an admin.
  10. On Enterprise the people who reach the source through a team are read from the members of its teams, with the team:read scope of the same update. Where it cannot be read no team is copied, and the teams are reported as TEAM_PERMISSIONS_NOT_PORTABLE.
  11. Crowdin marks no technical account in the members of a project: one is skipped when Crowdin refuses to add it, with TECHNICAL_DETECTION_UNAVAILABLE.
  12. On Enterprise the custom placeholders of the copy are named in the preview and the report by their description, or their definition, from the list of the organization, which the custom-placeholder:read scope of the same update reads. Until an admin applies it, or where Crowdin refuses that list, they are named by their numbers; what is copied stays the same.
  13. Every built-in placeholder switched off in the copy is one request to Crowdin, and one entry in the activity of the new project. crowdin.com no longer shows the custom placeholders some projects were given before it kept them to Enterprise, so those are not copied.
  14. Webhooks of the source are not copied, and neither are its pending invitations on crowdin.com (MEMBER_PENDING_SKIPPED): no second email is sent to anyone. On Enterprise a person invited to the organization who has not accepted yet already has the project access they were given, and the members listing of the project has nothing that marks them, so the copy gives them the same access.