Skip to main content

Command Palette

Search for a command to run...

API

Origin API Changelog

Changes to the Origin public API, including endpoints, request and response schemas, scopes, and webhooks, grouped by day with the newest first. Each change carries one label: Breaking, Deprecated, Added, Changed, or Removed. Breaking and deprecated changes include migration guidance inline. The Origin API reference always reflects the latest synced state.

  • Changed. Every operation in the published OpenAPI specification carries an x-origin-scopes extension that names the scope the operation requires and the credentials it accepts. scopes holds the required scope, and tokenTypes holds the accepted credential kinds: app for an app JWT, installation for an installation access token, and user for a user credential. A credential kind the operation rejects is absent from tokenTypes, and Get Rate Limit is the one operation that requires no scope. The extension also replaced the scope sentences that Create Label, Update Label, and Delete Label carried in their descriptions. Authorization is unchanged; the extension publishes the scopes Origin already enforced.
  • Added. List Pull Request Requested Reviewers returns the users and groups whose review is outstanding on a pull request: GET /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/requested_reviewers. A direct request clears when that user submits a review, and a group request clears when any current member of the group submits, while an unsubmitted draft review leaves the request pending. Reviewers come back as ids, and reading them requires repository:pull_requests:reviews:read.
  • Added. Request Pull Request Reviewers requests reviews from users and groups and returns the reviewers it requested: POST /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/requested_reviewers. Identify each reviewer by public user_… id, email, grp_… id, or group slug; display names do not resolve, an unknown or ambiguous identifier returns InvalidArgument (HTTP 400), and a reviewer who is not a candidate for the repository returns PermissionDenied (HTTP 403). Requesting an already-requested reviewer bumps the request, so a reviewer who had submitted a review reappears as pending. Requires repository:pull_requests:reviews:write.
  • Added. Remove Pull Request Requested Reviewers drops outstanding review requests and returns 204 with an empty body: DELETE /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/requested_reviewers. Removing a reviewer who is not currently requested is a no-op, and a stable public id still resolves after that reviewer leaves the repository's candidate list, so a stale request can be cleared. Requires repository:pull_requests:reviews:write.
  • Added. Update Pull Request Thread resolves or reopens a pull request comment thread and returns its updated state: PATCH /v1/origin/repos/{ownerSlug}/{repoName}/pulls/threads/{threadId}. Send resolved as true to resolve and false to reopen. Both directions are idempotent, replying to a resolved thread does not reopen it, and a thread stored on another repository returns 404. Requires repository:pull_requests:reviews:write.
  • Added. Pull request comments carry their full thread in place of a bare thread id. thread gained the filed-against version with its head and base SHAs, the path, side, startLine, and endLine of the thread's diff anchor, resolvedAt, and the thread's own createdAt and updatedAt. Returned by List Pull Request Comments, Get Pull Request Comment, and Update Pull Request Comment. thread.id is unchanged, so grouping comments by it keeps working.
  • Added. Create Pull Request Comment accepts an inline anchor, made of path, side, startLine, and an optional endLine, that opens a line-anchored thread on a pull request version's diff, plus a versionNumber naming the version to file against that defaults to the latest at call time. The path must be part of that version's diff on a side the file has content on, any line of a changed file anchors rather than only lines inside a diff hunk, and an invalid anchor returns InvalidArgument (HTTP 400) instead of falling back to a general-discussion comment. inline and threadId are mutually exclusive, as are threadId and versionNumber.
  • Added. Create Pull Request Review accepts a comments array, at most 50 per request, that publishes a review together with its comments in one atomic call. Each entry carries a body plus the same targets as Create Pull Request Comment: an inline anchor on the reviewed version's diff, a threadId reply, or neither for a new general-discussion thread. Every anchor is validated before anything is written, so a single bad anchor fails the whole request with InvalidArgument (HTTP 400) and publishes nothing. The operation carries no idempotency key, so read List Pull Request Reviews before retrying an ambiguous failure. A request without comments behaves as it did.
  • Added. pull_request.comment.created carries the thread's diff anchor on the comment that opened the thread, so a receiver can materialize the thread without a follow-up read. comment.thread holds the filed-against version, path, side, startLine, and endLine; a reply carries comment.thread.id alone, and resolution state stays out of the event. Comments filed with a review through Create Pull Request Review emit nothing until the review submits, and then each one emits its own event.
  • Added. Get Repo Tarball downloads a gzip-compressed tar of a repository tree: GET /v1/origin/repos/{ownerSlug}/{repoName}/tarball/{ref}. The first request for a resolved commit streams application/gzip as the response body; later requests for the same commit return 302 with a signed download URL in Location, valid for 15 minutes. Archive entries sit at the root of the tar with no wrapping directory, an empty repository returns ABORTED (HTTP 409 Conflict), and downloading an archive requires repository:contents:read.
  • Added. List Comparison Files lists the files a comparison changed, the diff of head against the merge base of base and head: GET /v1/origin/repos/{ownerSlug}/{repoName}/compare/{basehead}/files. Results are paginated, 30 files per page by default and at most 100, and each file carries the shape List Commit Files returns. An identical or behind comparison returns an empty list, unrelated histories return 404, and a comparison whose commits move mid-pagination rejects the page token with InvalidArgument (HTTP 400) so listing restarts from the first page. Reading comparison files requires repository:contents:read.
  • Added. The signing key endpoint sends Cache-Control: public, max-age=600, stale-if-error=600. Reuse a cached JWKS for 10 minutes, then refresh; when a refresh fails, keep the last good keys for at most another 10 minutes before failing verification. Refresh on a signature no active key verifies so a retired key ID drops out.
  • Changed. A check run still in_progress when its deadlineAt passes completes with a timed_out conclusion and delivers repository.check_run.completed, superseding the August 27 note that a deadline does not change a run's status. Expiry runs as a periodic sweep rather than a per-run timer, so a run can sit past its deadline briefly. A queued run never expires, nor does a run carrying no deadlineAt, and Origin leaves the run's externalUpdatedAt in place so a later completion from your provider can overwrite the timed_out conclusion.
  • Changed. repository.pushed is not delivered for a repository Origin mirrors from GitHub. GitHub owns those pushes and sends its own push webhooks, so the Origin delivery duplicated them. Pushes to native Origin repositories and to outbound mirrors are delivered unchanged, and mirror state affects no other event.
  • Changed. Create Pull Request rejects a head with no history in common with base with InvalidArgument (HTTP 400) and creates nothing, in place of the 404 the underlying comparison surfaced.
  • Changed. A push that leaves an open pull request's head with no history in common with its base closes the pull request and delivers pull_request.closed. A later related push does not reopen it.
  • Changed. Each operation in the OpenAPI specification documents the response codes that operation can return, in place of a uniform 400, 401, 403, and 429 on every operation: 404 on every parameterized path, 409 wherever a handler reports a conflict, 202 on Batch Redeliver Webhook Deliveries and Sync Mirror, and shorter sets on Get Rate Limit and Get Authenticated App. The Status schema describes the error envelope Origin returns, including that a 404 never distinguishes a missing resource from an unreachable one, and every operation carries a request and response example. Request handling is unchanged; regenerate any client built from the spec to pick up the new response models.
  • Added. Check runs accept and return an optional deadlineAt timestamp. Send it in the run body on Post Check Run or Batch Upsert Check Runs, and read it back on Get Check Run, List Check Runs For Suite, and List Check Runs For Commit. Origin clears the deadline once the run reaches completed, leaves the stored value unchanged when an update omits the field, and rejects a deadline more than 24 hours in the future with InvalidArgument (HTTP 400) rather than clamping it. A deadline does not change the run's status when it passes.
  • Changed. The published OpenAPI specification declares https://api.cursor.com as its server and a bearerAuth HTTP bearer security scheme, so a client generated from the document picks up the base URL and the Authorization: Bearer requirement.
  • Changed. OpenAPI path parameters carry the names the URLs already use. The generated identifier.ownerSlug and identifier.name bindings became ownerSlug and repoName across all 55 repository-scoped operations, which lets standard OpenAPI generators consume the document. Request URLs and request behavior are unchanged; regenerate any client built from the spec to pick up the new parameter names.
  • Changed. Published enums no longer list their *_UNSPECIFIED zero-value entries, such as RULESET_ENFORCEMENT_UNSPECIFIED on Create Ruleset and PULL_REQUEST_REVIEW_VERDICT_UNSPECIFIED on Create Pull Request Review. Origin never accepted or returned those values, so requests and responses are unchanged.
  • Changed. Every operation in the spec documents 400, 401, 403, and 429 responses carrying the google.rpc.Status body, in place of the catch-all default response alone. See Errors for the body and the full status list.
  • Breaking. Reviewer webhook payloads for pull_request.reviewer.added, pull_request.reviewer.removed, and pull_request.reviewer.rerequested replace the reviewer.kind and reviewer.id pair with a typed reviewer, where exactly one of reviewer.user or reviewer.group is present. Migration: read reviewer.user.id where you read reviewer.id with a reviewer.kind of user, and reviewer.group.id where reviewer.kind was group.
  • Added. List Labels returns the label definitions a repository owns, ordered by name: GET /v1/origin/repos/{ownerSlug}/{repoName}/labels. Reading labels requires the new repository:labels:read scope. Results are paginated, 30 labels per page by default and at most 100.
  • Added. Create Label defines a label on a repository and returns it: POST /v1/origin/repos/{ownerSlug}/{repoName}/labels. Every label write requires the new repository:labels:write scope. name is capped at 50 characters and description at 255, color must be six hexadecimal characters without a leading #, and a name another label on the repository already uses is rejected with AlreadyExists (HTTP 409 Conflict).
  • Added. Get Label returns one repository label by name: GET /v1/origin/repos/{ownerSlug}/{repoName}/labels/{labelName}. An unknown name returns 404.
  • Added. Delete Label deletes a repository label by name and returns 204: DELETE /v1/origin/repos/{ownerSlug}/{repoName}/labels/{labelName}. Deleting a label also removes it from every pull request it was assigned to.
  • Added. Update Label changes a label's name, color, or description, addressing it by its current name: PATCH /v1/origin/repos/{ownerSlug}/{repoName}/labels/{labelName}. Omitted fields are left unchanged, and renaming to a name another label already uses is rejected with AlreadyExists (HTTP 409 Conflict).
  • Added. List Check Run Annotations returns a check run's annotations in ascending ID order, which is also creation order: GET /v1/origin/repos/{ownerSlug}/{repoName}/check-runs/{checkRunId}/annotations. Reading them requires repository:checks:read. Results are paginated, 30 annotations per page by default and at most 100.
  • Added. Create Check Run Annotations appends between 1 and 25 annotations to a check run in one atomic batch and returns them: POST /v1/origin/repos/{ownerSlug}/{repoName}/check-runs/{checkRunId}/annotations. Appending requires repository:checks:write. A check run holds at most 100 annotations, and a batch that would exceed that is rejected with ResourceExhausted (HTTP 429) without writing anything. The operation is append-only and not idempotent, so a retry after an ambiguous failure can append duplicates.
  • Added. List Pull Requests takes five more query parameters: author, a public actor ID exactly as the response returns it in pullRequests[].author.user.id, pullRequests[].author.app.id, or pullRequests[].author.serviceAccount.id; base, an exact base-branch filter that accepts a short name or a fully qualified ref; direction, desc for newest first (the default) or asc for oldest first; and since and until, inclusive RFC 3339 bounds on creation time. An author with no pull requests returns an empty list, and any other unusable value returns InvalidArgument (HTTP 400).
  • Added. pull_request.review.dismissed is delivered when a submitted review is dismissed, whether explicitly or by being superseded by a newer decision. It carries the same payload shape as pull_request.review.submitted with review.dismissal populated, and subscribing to it needs repository:pull_requests:reviews:read.
  • Added. Installations name the user who installed the app. installedBy, carrying that user's public user_… ID and email, is returned by Get App Installation and List App Installations and rides every installation.* webhook snapshot; there it identifies the original installer and is omitted when that user record can no longer be read. The installation receipt gains an installedBy claim naming the user who performed that install or re-consent, so after a re-consent the two can differ.
  • Added. Ping Webhook sends a test delivery to your app's configured webhook URL and reports what the receiver answered: POST /v1/origin/app/webhook/pings. The delivery is signed like a production one, carries webhook-event-type of ping, and belongs to no installation. Origin sends it once with no retries, and it never appears in List Webhook Deliveries. An app with no webhook URL configured is rejected with FailedPrecondition (HTTP 400).
  • Added. Error responses carry the request ID twice: an X-Request-ID response header, and a google.rpc.RequestInfo entry in details. Origin echoes the x-request-id you sent or generates one when you send none, and includes the entry even when the message is an opaque internal error. See Errors.
  • Changed. Create Pull Request and Update Pull Request reject a title longer than 256 characters, or a body longer than 65,536 characters, with InvalidArgument (HTTP 400). Values past those lengths previously failed with an internal error. Both limits count Unicode code points, so an astral character such as an emoji counts once.
  • Changed. Sync Mirror responses always carry synced, true or false, mirroring the HTTP status: 200 when true, 202 when false. The field was previously omitted when false, so callers had to read an absent field as false.
  • Changed. Unmatched paths under /v1/origin, and requests that use the wrong method on a known path, return the documented error envelope rather than a generic router body. The message names the method and path and never echoes the query string.
  • Changed. Merging a pull request delivers a repository.pushed webhook for the base ref the merge advances. Origin performs that push itself, so the event names no pusher. Base-ref updates from a merge were previously not delivered.
  • Changed. Create Pull Request Comment and Update Pull Request Comment reject a body longer than 65,536 characters with InvalidArgument (HTTP 400). A body past that length previously failed with an internal error. The limit counts Unicode code points, so an astral character such as an emoji counts once.
  • Added. Delete Ruleset deletes a repository ruleset by its stable Origin ID and returns 204: DELETE /v1/origin/repos/{ownerSlug}/{repoName}/rulesets/{rulesetId}. Requires repository:rulesets:write. A ruleset stored on a different repository reads as an unknown ruleset, and an empty rulesetId is rejected with InvalidArgument (HTTP 400).
  • Added. Every repository-scoped endpoint addresses a repository by its stable ID as well as by owner and name: send _ as the owner slug and the ID as the repository name, as in GET /v1/origin/repos/_/REPO_ID. Read the ID from id on Get Repo. The ID survives a rename but grants nothing on its own, so your app needs the same scope on the resolved repository, and an ID it cannot reach returns the same 404 as an ID that does not exist. Create Repo takes an owner slug alone and rejects _.
  • Changed. Apps reach mirrored repositories. A mirror can be selected into an installation, appears in List App Installation Repositories and in the installation webhook payload repository arrays, can be named in repositoryIds on Create Installation Access Token, and receives webhook deliveries. Until a mirror becomes a stable outbound mirror it stays read-only: only repository:metadata:read and repository:contents:read apply, and every other scope returns 403 on that repository, including git push. See Mirrored repositories.
  • Breaking. The recursive query parameter on Get Tree is a boolean rather than a string, so only true and 1 walk the whole tree; every other value, including false, 0, and a bare ?recursive, lists immediate children only. Migration: send recursive=true wherever your integration relied on any non-empty recursive value enabling recursion.
  • Breaking. Pull request lifecycle webhook payloads omit the pull request's assigned labels, superseding the field announced on August 20, 2026. REST responses still carry it. Migration: read labels from Get Pull Request or List Pull Requests instead of the webhook snapshot.
  • Added. List Rulesets returns every ruleset configured on a repository plus one shared repository reference: GET /v1/origin/repos/{ownerSlug}/{repoName}/rulesets. Rulesets are bounded configuration, so the response is not paginated. Reading rulesets requires repository:rulesets:read.
  • Added. Create Ruleset stores a new ruleset and returns it with the IDs Origin assigns to each rule and bypass actor: POST /v1/origin/repos/{ownerSlug}/{repoName}/rulesets. Both ruleset write endpoints require repository:rulesets:write.
  • Added. Get Ruleset returns a single ruleset by its stable Origin ID: GET /v1/origin/repos/{ownerSlug}/{repoName}/rulesets/{rulesetId}.
  • Added. Update Ruleset replaces a ruleset's configuration in full, including its rules and bypassActors: PUT /v1/origin/repos/{ownerSlug}/{repoName}/rulesets/{rulesetId}. Send every rule and bypass actor you want to keep, because the stored entries are replaced rather than merged.
  • Added. Rulesets carry id, name, description, enforcement (active, evaluate, or disabled), kind (merge_branch, push_branch, push_tag, or push_repository), the includedRefNames and excludedRefNames patterns that accept globs plus the ~ALL and ~DEFAULT_BRANCH tokens, rules, and bypassActors. Create Ruleset and Update Ruleset reject more than 64 patterns per list, 20 rules, or 15 bypass actors with InvalidArgument (HTTP 400).
  • Added. Merge Pull Request accepts an optional expectedHeadSha request field, the full commit SHA the pull request's head must match. When the head has moved, the merge is rejected with ABORTED (HTTP 409 Conflict) and nothing merges; a value that is not a full commit SHA is rejected with InvalidArgument (HTTP 400). Omit it to merge whatever the current head is.
  • Added. Owner references carry a type string, team or user, omitted when Origin cannot resolve it. Returned wherever an owner or installation target appears, including Get Repo, List Repos, List App Installations, and the repository reference on check and pull request responses.
  • Added. List Pull Request Labels returns the labels assigned to a pull request, ordered by name: GET /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labels. Requires repository:pull_requests:read. Results are paginated, 30 labels per page by default, and at most 100 labels per page.
  • Added. Add Pull Request Labels assigns existing repository labels to a pull request and leaves the labels already on it in place: POST /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labels. Every label write endpoint requires repository:pull_requests:write.
  • Added. Set Pull Request Labels replaces every label on a pull request with the names you send, and an empty list clears them: PUT /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labels.
  • Added. Remove Pull Request Label removes one label by name and returns the labels left on the pull request: DELETE /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labels/{labelName}.
  • Added. Remove All Pull Request Labels clears every label from a pull request and returns 204: DELETE /v1/origin/repos/{ownerSlug}/{repoName}/pulls/{pullNumber}/labels.
  • Added. Label entries carry id, name, color as a six-character hex value without a leading #, and an optional description. Returned by every pull request label endpoint.
  • Changed. The app JWT rate limit budget rose from 600 to 6,000 points per minute, and Create Installation Access Token charges 1 point instead of 5, so an app can mint roughly 100 installation tokens per second.
  • Changed. Owner eligibility for Create Repo and pushes over Git HTTPS admits the Pro Student and Start plans alongside Pro, Pro+, and Ultra. Team-owner requirements are unchanged.
  • Changed. Owner slugs and repository names in repository paths resolve case-insensitively, and responses return the stored casing rather than the casing you sent. Create Repo rejects a name that differs only in case from one the owner already has, so compare repository names case-insensitively.
  • Breaking. Git over HTTPS rejects a push with 403 when the repository's owner is not eligible to write to Origin. A user owner must be on a Pro, Pro+, or Ultra plan, and a team owner must have an active paid team plan, must not be on Privacy Mode (Legacy), and must not have Origin turned off by a team admin. Clone, fetch, and pull are unaffected. Migration: handle 403 on push as an owner-eligibility failure that a retry cannot clear, and confirm the owner's plan before pushing on its behalf.
  • Changed. The first push to a repo created through Create Repo retargets defaultBranch when that push only creates branches and none of them is the stored default: Origin picks the created branch, or main or master when the push creates several and one of those names is among them. Read the current value from Get Repo.
  • Breaking. Create Repo rejects a request whose owner is not eligible to write to Origin, returning FailedPrecondition (HTTP 400). A user owner must be on a Pro, Pro+, or Ultra plan, and a team owner must have an active paid team plan, must not be on Privacy Mode (Legacy), and must not have Origin turned off by a team admin. Migration: handle 400 from Create Repo as an owner-eligibility failure that a retry cannot clear, and confirm the owner's plan before creating repositories on its behalf.
  • Breaking. Apps lost access to repositories that Origin mirrors in from GitHub. Those repositories no longer appear in List App Installation Repositories, Create Installation Access Token rejects them in repositoryIds, and a request that names one returns 403 over both the REST API and Git over HTTPS. Migration: discover repositories from List App Installation Repositories instead of a stored repository list, and read a GitHub-sourced repository from GitHub rather than the Origin API.
  • Breaking. Origin stopped sending webhooks for repositories it mirrors in from GitHub, and installation event payloads dropped those repositories from their selected repository arrays and repositoriesCount. Migration: source events for a GitHub-sourced repository from GitHub, and treat an installation payload's repository array as the set your app can reach.
  • Breaking. Reviewer webhook payloads carry a stable external ID in reviewer.id: the encoded user ID (user_…, the same format as the organization API) when kind is user, replacing the provider-scoped auth ID; group reviewers keep the group public ID (grp_…). Affects pull_request.reviewer.added, pull_request.reviewer.removed, and pull_request.reviewer.rerequested. Migration: match user reviewers by the encoded user_… ID wherever your integration compared reviewer.id against stored auth IDs.
  • Added. Apps can hold up to 10 active Ed25519 signing keys, and app JWT verification accepts a token signed with any active key.
  • Added. Sync Mirror synchronizes one ref of a mirrored repository from its upstream source: POST /v1/origin/repos/{ownerSlug}/{repoName}:syncMirror. Requires repository:contents:read and returns 200 when the sync target is satisfied or 202 while the sync is pending.
  • Added. Post-install redirects carry an installation_receipt query parameter: a five-minute Origin-signed JWT that identifies the installation in its sub claim and echoes the publisher's state as a claim. Verify it against the published JWKS before trusting the callback. See Installation receipt.
  • Removed. Origin actor objects no longer include the top-level kind and id fields, completing the deprecation announced on August 5, 2026. Every actor, author, and dismissedBy field across check, commit, and pull request responses is affected. Migration: read the user, app, or serviceAccount variant set on the actor.
  • Added. Get Rate Limit returns the authenticated principal's shared per-minute point budget without consuming points: GET /v1/origin/rate_limit. See Rate limits.
  • Deprecated. OriginActor.kind and OriginActor.id. Actor identity is a discriminated union of user, app, and serviceAccount variants. Migration: read the selected variant's fields instead of top-level kind and id.