Skip to content

Add a deploy key to a repository: key, one line in OpenSSH public key format (ssh-ed25519, ecdsa-sha2-nistp256/384/521 or ssh-rsa), and a title.

POST/repos/{owner}/{name}/keys

It is read-only unless read_only is false, which lets it push, workflow files included. A key registered anywhere already, as a person’s SSH key or another deploy key, is refused with 409: give each machine its own. At most 100 keys a repository. Needs the Admin role on the repository and a confirmed email address; agents’ tokens and workspace tokens without Admin are refused. Recorded in the workspace’s audit log.

read_only is true unless you send false. The key’s comment is not kept; it becomes the title when you send none. Refused with 400 for a line that is not an OpenSSH public key, 409 with Key is already in use. when the key is registered already (as anyone’s SSH key or as a deploy key anywhere), 409 past 100 keys, and 403 without the Admin role, from an agent’s token, from a workspace token without Admin, or before your email address is confirmed. Recorded in the workspace’s audit log as repo.deploy_key_added.

  • Authentication: Required. Send an access token as Authorization: Bearer.
  • MCP tool: access with action add_deploy_key, and the same inputs
  • Scope: An access token needs access:admin.
Name Type Required Description
owner string Yes The workspace that owns the repository.
name string Yes The repository’s name.

Send a JSON object. Names are snake_case, as in responses; the camelCase spelling is accepted too.

Name Type Required Description
title string No A name for it, such as the machine that uses it. Left out, the key’s comment, else “Deploy key”.
key string Yes The public key, one line in OpenSSH format: the contents of a .pub file.
read_only boolean No False lets it push, workflow files included. True (read-only) unless you say.
curl -X POST https://api.g1t.sh/repos/flagon-io/hello/keys \
-H "Authorization: Bearer $G1T_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Release bot",
"key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8Vq2nLr5Xw0sT3yK7mP1cH4dF6gB9aE2uZ5oN8iR0j release@ci",
"read_only": false
}'

A successful request answers 200 with:

{
"id": "dk_01kp3g4h5j6k7m8n9p0q1r2s3t",
"title": "Release bot",
"key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ8Vq2nLr5Xw0sT3yK7mP1cH4dF6gB9aE2uZ5oN8iR0j",
"fingerprint": "SHA256:2h7VyLkcqBHwKVtfFUchGsE5pDwpgBPV/VvJgYiS27M",
"read_only": false,
"created_at": "2026-10-07T15:30:00.000Z",
"created_by": "syntaqx",
"last_used_at": null
}

A failed request answers with one of these statuses and a body like {"error": {"code": "not_found", "message": "Repository not found."}}. See errors.

Status Code When
401 unauthenticated A token is required, or the one sent is not valid.
403 forbidden The token is valid but not allowed to do this, such as a member-only change or an agent token outside its repository.
404 not_found It does not exist, or you cannot see it.
409 conflict The request conflicts with the current state.
422 invalid The input is not valid. message says which field and why.