AmLang registry https://tucan.amlang.net/api

Issues API

Submit a feature request or bug report. No account, token, or development key is required.

Submit an issue

POST /issues accepts a JSON object with Content-Type: application/json. Every successful submission creates a new issue.

Each client IP may submit one issue per 60 seconds, across all issue types, email addresses, and visibility settings. Extra submissions return 429 with a Retry-After header containing the remaining wait in seconds. Invalid submissions do not consume the allowance, and blocked retries do not extend it. The limit is stored in the database and shared across API instances and restarts.

curl -X POST "https://tucan.amlang.net/api/issues" \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "email": "user@example.com",
    "title": "Add keyboard shortcuts for build targets",
    "description": "I would like to switch build targets without opening the menu.",
    "type": "feature_request",
    "visibility": "public"
  }'

For a bug report, use "type": "bug" and describe the steps to reproduce, expected behavior, actual behavior, and your platform and application version.

Required fields

fieldrequirements
emailValid contact email address, at most 254 characters.
titleNonempty text, at most 200 characters.
descriptionNonempty text, at most 10,000 characters. Newlines are preserved.
typeExactly feature_request or bug.

All four fields must be JSON strings. Leading and trailing whitespace is removed. Unknown fields are ignored. The server assigns id and createdAt. Submitting an email does not create an account, verify the address, or send an email.

Optional visibility is public or private; omitted means private. New issues always have approvalStatus: "pending_approval" and status: "suggestion". Sending approvalStatus or status on submission returns 400; only an administrator can set these fields.

Response

201 Created returns the saved issue as JSON:

{
  "id": 42,
  "email": "user@example.com",
  "title": "Add keyboard shortcuts for build targets",
  "description": "I would like to switch build targets without opening the menu.",
  "type": "feature_request",
  "visibility": "public",
  "approvalStatus": "pending_approval",
  "status": "suggestion",
  "createdAt": "2026-09-21T10:00:00Z"
}

Keep the returned ID as a reference. Retrying a successful request after the rate-limit interval creates another issue.

Public issues

curl "https://tucan.amlang.net/api/issues"
curl "https://tucan.amlang.net/api/issues/42"

GET /issues returns a JSON array of all issues that are both visibility: "public" and approvalStatus: "approved", newest first (or []). GET /issues/{id} returns one issue with the same restriction. No authentication is needed. These responses include the issue fields shown above except email, which is never included in public reads.

Private, pending, rejected, and nonexistent issues all return 404 on public reads, even when an admin token is supplied. Query parameters cannot bypass the visibility and approval checks. Marking an issue private or withdrawing approval removes it from subsequent public reads; responses use Cache-Control: no-store.

The title and description of an approved public issue are public content. Anonymous submitters cannot edit issues after submission.

Moderation

These endpoints require the existing X-Admin-Token header. Admin responses include email addresses, ipAddress, and all visibility and approval states.

The server records the submission address in the ip_address database column. It prefers X-Real-IP, which the edge nginx overwrites with $remote_addr. This avoids trusting a caller-supplied first address in X-Forwarded-For, to which nginx appends the client address. If X-Real-IP is absent, the API falls back to the first address in X-Forwarded-For, then the connection's remote address. This trust model assumes requests come through the configured nginx; direct API access must be restricted before relying on these headers for IP blocking. Missing or oversized addresses, and issues created before IP recording, have a null address. IP addresses are excluded from submission and public read responses. Request JSON cannot set or change the recorded address. The same address is used for the submission rate limit; submissions with no usable recorded address share a single fallback allowance.

curl "https://tucan.amlang.net/api/admin/issues" -H "X-Admin-Token: $ADMIN_TOKEN"
curl "https://tucan.amlang.net/api/admin/issues/42" -H "X-Admin-Token: $ADMIN_TOKEN"
curl -X PATCH "https://tucan.amlang.net/api/admin/issues/42" \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary '{"approvalStatus":"approved"}'

PATCH /admin/issues/{id} accepts any combination of visibility, approvalStatus, and status (at least one is required). Visibility is public or private; approval status is pending_approval, approved, or rejected. Omitted fields are preserved. Approval alone does not make a private issue public. Successful moderation returns 200 with the updated issue. Other fields, including the email and issue text, are not changed by this endpoint.

Workflow status

status tracks the work independently of approvalStatus. It is returned in submission, public, and admin responses. New issues start as suggestion. Administrators can move an issue to any of these stages:

status valuestage
suggestionSuggestion
backlogBacklog
in_progressIn progress
in_reviewIn review
in_testingIn testing
doneDone
curl -X PATCH "https://tucan.amlang.net/api/admin/issues/42" \
  -H "X-Admin-Token: $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  --data-binary '{"status":"in_progress"}'

Changing workflow status does not change approval or visibility. Even a done issue is only publicly readable when it is public and approved. Invalid updates return 400 without changing any field.

Errors

statusmeaning
400Invalid JSON object, missing or invalid field, unsupported type, or text exceeding its limit. No issue is created.
415Send the request with Content-Type: application/json.
429This IP has submitted an issue within the last 60 seconds. Wait for the number of seconds in Retry-After before retrying.
403Missing or invalid admin token on an admin endpoint.
404Issue does not exist, or is not publicly available through the public endpoint.

Errors return JSON describing the first validation failure, for example:

{"error":"type must be feature_request or bug"}