> ## Documentation Index
> Fetch the complete documentation index at: https://developers.phrase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Auto-sync translation keys on code push

> Set up a Phrase Strings repo sync so a push to your git repository imports new and changed translation keys on its own, and fix the errors that stop it.

Stop pushing keys to Phrase by hand. Connect a Phrase project to your git repository so that every push imports new and changed translation keys automatically, with no code in your pipeline.

## Quickstart

```bash theme={null}
curl --request GET \
  --url https://api.phrase.com/v2/projects/<PROJECT_ID> \
  --header 'Authorization: Bearer <JWT_TOKEN>'
```

## Prerequisites

1. Authentication set up. See the [Authentication guide](/en/api/strings/authentication). The token needs the write scope to create a repo sync.
2. A project ID, the project code taken from the project in Phrase.
3. Your account ID. Repo sync calls are scoped to it.
4. A `.phrase.yml` file in the repository. It maps files to languages; run `phrase init` to scaffold one.
5. Git provider access: a personal access token for GitHub, GitLab, or Bitbucket, or the Phrase GitHub App installed.

## How-to

A repo sync is the connection between one Phrase project and one git repository. With auto-import on, a push to the base branch triggers Phrase to read the changed files and import their keys. The sync is created once; after that the import runs on its own.

1. Create the repo sync with auto-import on. Create the sync against your account, pointing it at the project and repository. The `project_id`, `repo_name`, and `connection_type` fields are required. Set `auto_import` to `true` so pushes trigger an import. Use `connection_type` token with an `access_token` field for a personal access token, or `github_app` if the Phrase GitHub App is installed. If creation returns 403, the account lacks the repo sync feature, the token lacks the write scope, or you lack permission to manage syncs. A 422 means a field failed validation, often a `repo_name` not in `owner/repo` form; the `errors` array names it, so correct it and retry.

   ```bash theme={null}
   curl --request POST \
     --url https://api.phrase.com/v2/accounts/<ACCOUNT_ID>/repo_syncs \
     --header 'Authorization: Bearer <JWT_TOKEN>' \
     --header 'Content-Type: application/json' \
     --data '{
       "project_id": "<PROJECT_ID>",
       "git_provider": "github",
       "connection_type": "github_app",
       "repo_name": "acme/mobile-app",
       "base_branch": "main",
       "auto_import": true
     }'
   ```

2. Register the webhook on the repository. Auto-import needs a push webhook on the git side so the provider tells Phrase about each push. Add the webhook Phrase generates for the sync to the repository settings, and make sure the shared secret matches. This step happens in the git provider, not the API. If pushes never trigger an import, the most common cause is a webhook secret that does not match the one Phrase stored, so re-copy it. A push to a branch other than the base branch is ignored on purpose.

3. Confirm the import ran. Each push creates an import event on the sync. Read the event history to confirm the import ran and check its outcome. Each event has a type of import or export, a status of running, success, or failure, and an `auto_import` flag showing whether a push triggered it. On a failure, the `errors` array explains why, so you can fix the file or config and push again.

   ```bash theme={null}
   curl --request GET \
     --url https://api.phrase.com/v2/accounts/<ACCOUNT_ID>/repo_syncs/<REPO_SYNC_ID>/events \
     --header 'Authorization: Bearer <JWT_TOKEN>'
   ```

4. When a CI upload fits better. To push keys from your pipeline instead of a git webhook, upload the file directly. Use the file upload endpoint, which imports a whole file in one request, rather than a loop that creates one key at a time. Repo sync and direct upload are two paths to the same result, so pick one.

   ```bash theme={null}
   curl --request POST \
     --url https://api.phrase.com/v2/projects/<PROJECT_ID>/uploads \
     --header 'Authorization: Bearer <JWT_TOKEN>' \
     --form file=@locales/en.json \
     --form file_format=json \
     --form locale_id=<SOURCE_LOCALE_ID>
   ```

## When it breaks: common errors

A 4xx response tells you what to fix. For a 422, the `errors` array names the exact field.

<Card title="403: forbidden">
  The account lacks the repo sync feature, the token lacks the write scope, or you lack permission to manage syncs. Enable the feature or use a write-scoped token.
</Card>

<Card title="422: invalid field">
  A field failed validation, often a `repo_name` that is not in `owner/repo` form. The `errors` array names it, so fix it and retry.
</Card>

<Card title="404: not found">
  The account or repo sync ID does not resolve. Check each ID in the path and that the token can access it.
</Card>

<Card title="No import on push">
  A push did not import, usually because the webhook secret does not match Phrase's, or you pushed to a branch other than the base branch. Re-copy the secret; push to the base branch.
</Card>

<Card title="429: rate limited">
  A `429` means too many requests in a short time: wait for the window to reset, then retry. Webhooks are exempt.
</Card>

## Troubleshooting

<Card title="Status">
  Check the [Phrase status page](https://status.phrase.com) before assuming your own request is wrong.
</Card>

<Card title="Where to get help">
  If Phrase is healthy and the `errors` array does not explain the failure, [contact support](https://phrase.com/contact/).
</Card>

<Card title="Help center">
  [Support articles and answers](https://support.phrase.com/) to common Phrase questions.
</Card>

## Next steps

1. Review the reference pages this guide calls: [Create a repo sync](/en/api/strings/repo-syncs/create-a-repo-sync), [Repository syncs history](/en/api/strings/repo-sync-events/repository-syncs-history), and [Upload a new file](/en/api/strings/uploads/upload-a-new-file).
2. To wire the full loop from push to live translations, see Build your CI/CD pipeline.

*Last reviewed: September 18, 2026. API changes are recorded in the [changelog](/en/changelog).*
