---
title: "Forgejo (codeberg.org, self-served Swagger 2.0): spec vs live"
date: 2026-10-30
canonical: https://apidrifter.com/research/public-api-spec-drift-2026-11/codeberg/
part_of: https://apidrifter.com/research/public-api-spec-drift-2026-11/
---

# Forgejo (codeberg.org, self-served Swagger 2.0): spec vs live

Part of [We checked 13 public APIs against their own OpenAPI specs. 9 had hard mismatches.](https://apidrifter.com/research/public-api-spec-drift-2026-11/).

## Summary

- Spec: https://codeberg.org/swagger.v1.json (Swagger 2.0, converted to OpenAPI 3 before checking)
- Base URL: `https://codeberg.org/api/v1`
- Run: 2026-10-30, read-only GET requests, no credentials
- Coverage: 506 operations in the spec, 40 requests sent, 16 successful, 38 operations observed
- Hard violations: 23 distinct schema locations
- Reviewer note: Same drift as Gitea (Forgejo fork), plus repo.parent null on feeds.

## Hard violations (23)

A typed client generated from the spec would break on these.

- `response-null-not-allowed` (hard, breaking) at `GET /orgs/{org}/activities/feeds > response 200 > application/json > /0/comment/user`
  - response body at /0/comment/user: value is null but the schema does not allow null (and 14 more like it)
  - Request: `GET /orgs/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/comment/user.
  - If the implementation is the source of truth: Allow null at paths["/orgs/{org}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["comment"].properties["user"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /orgs/{org}/activities/feeds > response 200 > application/json > /0/repo/parent`
  - response body at /0/repo/parent: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /orgs/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repo/parent.
  - If the implementation is the source of truth: Allow null at paths["/orgs/{org}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["repo"].properties["parent"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /orgs/{org}/activities/feeds > response 200 > application/json > /0/repo/repo_transfer`
  - response body at /0/repo/repo_transfer: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /orgs/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repo/repo_transfer.
  - If the implementation is the source of truth: Allow null at paths["/orgs/{org}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["repo"].properties["repo_transfer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /orgs/{org}/activities/feeds > response 200 > application/json > /5/comment`
  - response body at /5/comment: value is null but the schema does not allow null (and 14 more like it)
  - Request: `GET /orgs/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /5/comment.
  - If the implementation is the source of truth: Allow null at paths["/orgs/{org}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["comment"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /orgs/{org}/repos > response 200 > application/json > /0/parent`
  - response body at /0/parent: value is null but the schema does not allow null (and 19 more like it)
  - Request: `GET /orgs/forgejo/repos -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/parent.
  - If the implementation is the source of truth: Allow null at paths["/orgs/{org}/repos"].get.responses["200"].content["application/json"].schema.items.properties["parent"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /orgs/{org}/repos > response 200 > application/json > /0/repo_transfer`
  - response body at /0/repo_transfer: value is null but the schema does not allow null (and 19 more like it)
  - Request: `GET /orgs/forgejo/repos -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repo_transfer.
  - If the implementation is the source of truth: Allow null at paths["/orgs/{org}/repos"].get.responses["200"].content["application/json"].schema.items.properties["repo_transfer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /packages/{owner} > response 200 > application/json > /0/repository/parent`
  - response body at /0/repository/parent: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /packages/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repository/parent.
  - If the implementation is the source of truth: Allow null at paths["/packages/{owner}"].get.responses["200"].content["application/json"].schema.items.properties["repository"].properties["parent"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /packages/{owner} > response 200 > application/json > /0/repository/repo_transfer`
  - response body at /0/repository/repo_transfer: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /packages/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repository/repo_transfer.
  - If the implementation is the source of truth: Allow null at paths["/packages/{owner}"].get.responses["200"].content["application/json"].schema.items.properties["repository"].properties["repo_transfer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo} > response 200 > application/json > /parent`
  - response body at /parent: value is null but the schema does not allow null
  - Request: `GET /repos/forgejo/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /parent.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}"].get.responses["200"].content["application/json"].schema.properties["parent"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo} > response 200 > application/json > /repo_transfer`
  - response body at /repo_transfer: value is null but the schema does not allow null
  - Request: `GET /repos/forgejo/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /repo_transfer.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}"].get.responses["200"].content["application/json"].schema.properties["repo_transfer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/activities/feeds > response 200 > application/json > /0/comment/user`
  - response body at /0/comment/user: value is null but the schema does not allow null (and 24 more like it)
  - Request: `GET /repos/forgejo/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/comment/user.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["comment"].properties["user"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/activities/feeds > response 200 > application/json > /0/repo/parent`
  - response body at /0/repo/parent: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repo/parent.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["repo"].properties["parent"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/activities/feeds > response 200 > application/json > /0/repo/repo_transfer`
  - response body at /0/repo/repo_transfer: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/repo/repo_transfer.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["repo"].properties["repo_transfer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/activities/feeds > response 200 > application/json > /6/comment`
  - response body at /6/comment: value is null but the schema does not allow null (and 4 more like it)
  - Request: `GET /repos/forgejo/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /6/comment.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["comment"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches > response 200 > application/json > /0/commit/added`
  - response body at /0/commit/added: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/branches -> 200 application/json;charset=utf-8`
  - Expected: `["array"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/commit/added.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches"].get.responses["200"].content["application/json"].schema.items.properties["commit"].properties["added"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches > response 200 > application/json > /0/commit/modified`
  - response body at /0/commit/modified: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/branches -> 200 application/json;charset=utf-8`
  - Expected: `["array"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/commit/modified.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches"].get.responses["200"].content["application/json"].schema.items.properties["commit"].properties["modified"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches > response 200 > application/json > /0/commit/removed`
  - response body at /0/commit/removed: value is null but the schema does not allow null (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/branches -> 200 application/json;charset=utf-8`
  - Expected: `["array"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/commit/removed.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches"].get.responses["200"].content["application/json"].schema.items.properties["commit"].properties["removed"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches > response 200 > application/json > /0/commit/verification/signer`
  - response body at /0/commit/verification/signer: value is null but the schema does not allow null (and 17 more like it)
  - Request: `GET /repos/forgejo/forgejo/branches -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/commit/verification/signer.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches"].get.responses["200"].content["application/json"].schema.items.properties["commit"].properties["verification"].properties["signer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches/{branch} > response 200 > application/json > /commit/added`
  - response body at /commit/added: value is null but the schema does not allow null
  - Request: `GET /repos/forgejo/forgejo/branches/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["array"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /commit/added.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches/{branch}"].get.responses["200"].content["application/json"].schema.properties["commit"].properties["added"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches/{branch} > response 200 > application/json > /commit/modified`
  - response body at /commit/modified: value is null but the schema does not allow null
  - Request: `GET /repos/forgejo/forgejo/branches/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["array"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /commit/modified.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches/{branch}"].get.responses["200"].content["application/json"].schema.properties["commit"].properties["modified"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches/{branch} > response 200 > application/json > /commit/removed`
  - response body at /commit/removed: value is null but the schema does not allow null
  - Request: `GET /repos/forgejo/forgejo/branches/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["array"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /commit/removed.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches/{branch}"].get.responses["200"].content["application/json"].schema.properties["commit"].properties["removed"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/branches/{branch} > response 200 > application/json > /commit/verification/signer`
  - response body at /commit/verification/signer: value is null but the schema does not allow null
  - Request: `GET /repos/forgejo/forgejo/branches/forgejo -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /commit/verification/signer.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/branches/{branch}"].get.responses["200"].content["application/json"].schema.properties["commit"].properties["verification"].properties["signer"] (add "null" to type, or nullable: true in OpenAPI 3.0).
- `response-null-not-allowed` (hard, breaking) at `GET /repos/{owner}/{repo}/commits > response 200 > application/json > /0/commit/verification/signer`
  - response body at /0/commit/verification/signer: value is null but the schema does not allow null (and 26 more like it)
  - Request: `GET /repos/forgejo/forgejo/commits -> 200 application/json;charset=utf-8`
  - Expected: `["object"]`
  - Actual: `null`
  - If the spec is the source of truth: Return a non-null value in response body at /0/commit/verification/signer.
  - If the implementation is the source of truth: Allow null at paths["/repos/{owner}/{repo}/commits"].get.responses["200"].content["application/json"].schema.items.properties["commit"].properties["verification"].properties["signer"] (add "null" to type, or nullable: true in OpenAPI 3.0).

## Other body findings (12)

Undocumented fields and loose constraints on successful responses. Documentation gaps, not violations.

- `response-undocumented-property` (body, info) at `GET /orgs/{org}/activities/feeds > response 200 > application/json > /0/act_user/username`
  - response body at /0/act_user/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /orgs/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /orgs/{org}/activities/feeds if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/orgs/{org}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["act_user"].properties.
- `response-undocumented-property` (body, info) at `GET /orgs/{org}/activities/feeds > response 200 > application/json > /0/repo/owner/username`
  - response body at /0/repo/owner/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /orgs/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /orgs/{org}/activities/feeds if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/orgs/{org}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["repo"].properties["owner"].properties.
- `response-undocumented-property` (body, info) at `GET /orgs/{org}/public_members > response 200 > application/json > /0/username`
  - response body at /0/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /orgs/forgejo/public_members -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /orgs/{org}/public_members if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/orgs/{org}/public_members"].get.responses["200"].content["application/json"].schema.items.properties.
- `response-undocumented-property` (body, info) at `GET /orgs/{org}/repos > response 200 > application/json > /0/owner/username`
  - response body at /0/owner/username: property "username" is not documented (and 19 more like it)
  - Request: `GET /orgs/forgejo/repos -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /orgs/{org}/repos if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/orgs/{org}/repos"].get.responses["200"].content["application/json"].schema.items.properties["owner"].properties.
- `response-undocumented-property` (body, info) at `GET /packages/{owner} > response 200 > application/json > /0/creator/username`
  - response body at /0/creator/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /packages/forgejo -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /packages/{owner} if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/packages/{owner}"].get.responses["200"].content["application/json"].schema.items.properties["creator"].properties.
- `response-undocumented-property` (body, info) at `GET /packages/{owner} > response 200 > application/json > /0/owner/username`
  - response body at /0/owner/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /packages/forgejo -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /packages/{owner} if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/packages/{owner}"].get.responses["200"].content["application/json"].schema.items.properties["owner"].properties.
- `response-undocumented-property` (body, info) at `GET /packages/{owner} > response 200 > application/json > /0/repository/owner/username`
  - response body at /0/repository/owner/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /packages/forgejo -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /packages/{owner} if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/packages/{owner}"].get.responses["200"].content["application/json"].schema.items.properties["repository"].properties["owner"].properties.
- `response-undocumented-property` (body, info) at `GET /repos/{owner}/{repo} > response 200 > application/json > /owner/username`
  - response body at /owner/username: property "username" is not documented
  - Request: `GET /repos/forgejo/forgejo -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /repos/{owner}/{repo} if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/repos/{owner}/{repo}"].get.responses["200"].content["application/json"].schema.properties["owner"].properties.
- `response-undocumented-property` (body, info) at `GET /repos/{owner}/{repo}/activities/feeds > response 200 > application/json > /0/act_user/username`
  - response body at /0/act_user/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /repos/{owner}/{repo}/activities/feeds if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/repos/{owner}/{repo}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["act_user"].properties.
- `response-undocumented-property` (body, info) at `GET /repos/{owner}/{repo}/activities/feeds > response 200 > application/json > /0/repo/owner/username`
  - response body at /0/repo/owner/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/activities/feeds -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /repos/{owner}/{repo}/activities/feeds if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/repos/{owner}/{repo}/activities/feeds"].get.responses["200"].content["application/json"].schema.items.properties["repo"].properties["owner"].properties.
- `response-undocumented-property` (body, info) at `GET /repos/{owner}/{repo}/commits > response 200 > application/json > /0/author/username`
  - response body at /0/author/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/commits -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /repos/{owner}/{repo}/commits if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/repos/{owner}/{repo}/commits"].get.responses["200"].content["application/json"].schema.items.properties["author"].properties.
- `response-undocumented-property` (body, info) at `GET /repos/{owner}/{repo}/commits > response 200 > application/json > /0/committer/username`
  - response body at /0/committer/username: property "username" is not documented (and 29 more like it)
  - Request: `GET /repos/forgejo/forgejo/commits -> 200 application/json;charset=utf-8`
  - Actual: `"username"`
  - If the spec is the source of truth: Stop returning "username" from GET /repos/{owner}/{repo}/commits if it is not part of the contract.
  - If the implementation is the source of truth: Add "username" to paths["/repos/{owner}/{repo}/commits"].get.responses["200"].content["application/json"].schema.items.properties["committer"].properties.

## Undocumented error responses (18)

3xx and 4xx statuses the spec does not mention, usually auth or not-found cases.

- `undocumented-status` (errors, breaking) at `GET /actions/run > response 403`
  - status 403 is not documented for "GET /actions/run"
  - Request: `GET /actions/run -> 403 application/json;charset=utf-8`
  - Expected: `["200"]`
  - Actual: `403`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /actions/run.
  - If the implementation is the source of truth: Add a "403" response under paths["/actions/run"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /activitypub/actor > response 404`
  - status 404 is not documented for "GET /activitypub/actor"
  - Request: `GET /activitypub/actor -> 404 text/plain; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `404`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /activitypub/actor.
  - If the implementation is the source of truth: Add a "404" response under paths["/activitypub/actor"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /nodeinfo > response 404`
  - status 404 is not documented for "GET /nodeinfo"
  - Request: `GET /nodeinfo -> 404 text/plain; charset=utf-8`
  - Expected: `["200"]`
  - Actual: `404`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /nodeinfo.
  - If the implementation is the source of truth: Add a "404" response under paths["/nodeinfo"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /orgs/{org}/labels/{id} > response 401`
  - status 401 is not documented for "GET /orgs/{org}/labels/{id}"
  - Request: `GET /orgs/forgejo/labels/1 -> 401 application/json;charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /orgs/{org}/labels/{id}.
  - If the implementation is the source of truth: Add a "401" response under paths["/orgs/{org}/labels/{id}"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /orgs/{org}/list_blocked > response 401`
  - status 401 is not documented for "GET /orgs/{org}/list_blocked"
  - Request: `GET /orgs/forgejo/list_blocked -> 401 application/json;charset=utf-8`
  - Expected: `["200"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /orgs/{org}/list_blocked.
  - If the implementation is the source of truth: Add a "401" response under paths["/orgs/{org}/list_blocked"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /orgs/{org}/quota > response 401`
  - status 401 is not documented for "GET /orgs/{org}/quota"
  - Request: `GET /orgs/forgejo/quota -> 401 application/json;charset=utf-8`
  - Expected: `["200","403","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 403, 404) from GET /orgs/{org}/quota.
  - If the implementation is the source of truth: Add a "401" response under paths["/orgs/{org}/quota"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /orgs/{org}/quota/artifacts > response 401`
  - status 401 is not documented for "GET /orgs/{org}/quota/artifacts"
  - Request: `GET /orgs/forgejo/quota/artifacts -> 401 application/json;charset=utf-8`
  - Expected: `["200","403","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 403, 404) from GET /orgs/{org}/quota/artifacts.
  - If the implementation is the source of truth: Add a "401" response under paths["/orgs/{org}/quota/artifacts"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /orgs/{org}/quota/attachments > response 401`
  - status 401 is not documented for "GET /orgs/{org}/quota/attachments"
  - Request: `GET /orgs/forgejo/quota/attachments -> 401 application/json;charset=utf-8`
  - Expected: `["200","403","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 403, 404) from GET /orgs/{org}/quota/attachments.
  - If the implementation is the source of truth: Add a "401" response under paths["/orgs/{org}/quota/attachments"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /orgs/{org}/quota/packages > response 401`
  - status 401 is not documented for "GET /orgs/{org}/quota/packages"
  - Request: `GET /orgs/forgejo/quota/packages -> 401 application/json;charset=utf-8`
  - Expected: `["200","403","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 403, 404) from GET /orgs/{org}/quota/packages.
  - If the implementation is the source of truth: Add a "401" response under paths["/orgs/{org}/quota/packages"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/actions/runners > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/actions/runners"
  - Request: `GET /repos/forgejo/forgejo/actions/runners -> 401 application/json;charset=utf-8`
  - Expected: `["200","400","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 400, 404) from GET /repos/{owner}/{repo}/actions/runners.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/actions/runners"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/actions/runners/jobs > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/actions/runners/jobs"
  - Request: `GET /repos/forgejo/forgejo/actions/runners/jobs -> 401 application/json;charset=utf-8`
  - Expected: `["200","403"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 403) from GET /repos/{owner}/{repo}/actions/runners/jobs.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/actions/runners/jobs"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/actions/runners/registration-token > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/actions/runners/registration-token"
  - Request: `GET /repos/forgejo/forgejo/actions/runners/registration-token -> 401 application/json;charset=utf-8`
  - Expected: `["200"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/actions/runners/registration-token.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/actions/runners/registration-token"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/actions/secrets > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/actions/secrets"
  - Request: `GET /repos/forgejo/forgejo/actions/secrets -> 401 application/json;charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/actions/secrets.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/actions/secrets"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/actions/variables > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/actions/variables"
  - Request: `GET /repos/forgejo/forgejo/actions/variables -> 401 application/json;charset=utf-8`
  - Expected: `["200","400","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 400, 404) from GET /repos/{owner}/{repo}/actions/variables.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/actions/variables"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/assignees > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/assignees"
  - Request: `GET /repos/forgejo/forgejo/assignees -> 401 application/json;charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/assignees.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/assignees"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branch_protections > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branch_protections"
  - Request: `GET /repos/forgejo/forgejo/branch_protections -> 401 application/json;charset=utf-8`
  - Expected: `["200"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200) from GET /repos/{owner}/{repo}/branch_protections.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branch_protections"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/branch_protections/{name} > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/branch_protections/{name}"
  - Request: `GET /repos/forgejo/forgejo/branch_protections/forgejo -> 401 application/json;charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/branch_protections/{name}.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/branch_protections/{name}"].get.responses.
- `undocumented-status` (errors, breaking) at `GET /repos/{owner}/{repo}/collaborators > response 401`
  - status 401 is not documented for "GET /repos/{owner}/{repo}/collaborators"
  - Request: `GET /repos/forgejo/forgejo/collaborators -> 401 application/json;charset=utf-8`
  - Expected: `["200","404"]`
  - Actual: `401`
  - If the spec is the source of truth: Return one of the documented statuses (200, 404) from GET /repos/{owner}/{repo}/collaborators.
  - If the implementation is the source of truth: Add a "401" response under paths["/repos/{owner}/{repo}/collaborators"].get.responses.

## Our input (1)

Our own request values were wrong. Not the API's fault; listed for honesty.

- `deprecated-operation-in-use` (input, info) at `GET /repos/{owner}/{repo}/actions/runners/registration-token`
  - "GET /repos/{owner}/{repo}/actions/runners/registration-token" is deprecated in the spec but still in use
  - Request: `GET /repos/forgejo/forgejo/actions/runners/registration-token -> 401 application/json;charset=utf-8`
  - If the spec is the source of truth: Migrate callers of GET /repos/{owner}/{repo}/actions/runners/registration-token to its replacement before removing it.
  - If the implementation is the source of truth: Remove deprecated: true from paths["/repos/{owner}/{repo}/actions/runners/registration-token"].get if the operation is still supported.

## Reproduce

```sh
curl -s -H 'Accept: application/json' 'https://codeberg.org/api/v1/orgs/forgejo/activities/feeds'
```

## Data

- Findings: https://github.com/apidrifter/apidrifter/tree/main/tools/survey/results/codeberg.findings.json
- Requests sent: https://github.com/apidrifter/apidrifter/tree/main/tools/survey/results/codeberg.observations.json
- Tool: https://apidrifter.com/docs/cli/ (reproduce with apidrifter run; target definition in tools/survey/targets.json)
