Each sr.ht API follows the same set of design conventions throughout. Each API is RESTful, authenticated with meta.sr.ht, and has consistent standards for pagination, response codes, and so on, throughout.
API wrappers for a few programming languages exist:
Please write to sr.ht-discuss to share yours!
All services are authenticated with OAuth via meta.sr.ht. For more information, consult the meta.sr.ht OAuth documentation.
The basic routing model is:
List of :resources
, e.g. /api/repos
Manipulate a specific :resource
by :id
.
List of :subresources
which are children of :resource
.
OR
A singleton which is owned by :resource
.
OR
A named action to be completed asynchronously.
Manipulate a specific :resource
by :id
. See /api/:resource/:id
.
:id
may be an integer or stringAll requests and response bodies shall be encoded with Content-Type
application/json
.
Each resource returned by the API has its own schema, and may be given in two forms: full form and short form. The full form is always returned when retrieving a resource by ID, and contains the maximum amount of detail. The short form contains less information — an ID at the minimum, but often more - and is returned where the long form would be inconvenient, such as from a list endpoint or for singletons embedded in a parent resource.
Timestamps are encoded as strings using the format %Y-%m-%dT%H:%M:%S
.
When executing a GET
request on a list of resources, the response format is:
{
"next": :id,
"results": [ :resources... ],
"results_per_page": 50,
"total": 200
}
To retrieve the next page of results, add ?start=:id
to your request URL,
using the :id
given by "next"
.
The use of HTTP response codes is standardized throughout sourcehut.
Errors are returned with a consistent response body:
{
"errors": [
{
"field": "example",
"reason": "example is required"
}
]
}
"field"
is omitted when it is not applicable.
Returns the API version installed for this service. Sourcehut uses semantic versioning, each version being of the format "major.minor.patch". The patch number increments with every release, the minor number increments when new features are added, and the major version increments on breaking changes. Note that the minor and patch versions may increment when changes are made to the web frontend — which may not necessarily affect the API — but major versions only increment on breaking API changes. Any API whose major version is 0 makes no guarantees about interface stability.
Changes considered non-breaking are adding new API endpoints and resources, adding members to existing resources, adding members to enumerations, and adding optional fields to existing requests.
Response
{
"version": "1.2.3"
}
full form
{
"canonical_name": "~username",
"name": "username",
"email": "email",
"url": "url" or null,
"location": "location" or null,
"bio": "bio" or null
}
short form
{
"canonical_name": "~username",
"name": "username"
}
Most resources will have webhooks which can deliver updates to your application
via HTTP POST. You'll able to subscribe to some number of events, such as (on
meta.sr.ht) profile:update
or ssh-key:remove
. These require the same OAuth
scopes to configure as are necessary to obtain these resources through polling -
there's no separate OAuth scope for webhooks.
Periodically polling the API is discouraged — use webhooks instead.
When the events you've subscribed to occur, the notification will be delivered
to your URL as an HTTP POST, generally in the same format as the affected
resource is encoded via the API. X-Webhook-Delivery
is set to a UUID assigned
to that webhook delivery, and X-Webhook-Event
is set to the specific event
that occurred, e.g. profile:update
.
The X-Payload-Signature
and X-Payload-Nonce
headers can be used to verify
the authenticity of the webhook payload. Concatenate the request body with the
nonce (treat the nonce as an ASCII-encoded string) and use it to verify the
base64-encoded Ed25519 signature given by the X-Payload-Signature
header. The
public key (also base64 encoded) is
uX7KWyyDNMaBma4aVbJ/cbUQpdjqczuCyK/HxzV/u+4=
. Here's an example of verifying
the payload in Python:
import base64
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
public_key = Ed25519PublicKey.from_public_bytes(
base64.b64decode('uX7KWyyDNMaBma4aVbJ/cbUQpdjqczuCyK/HxzV/u+4='))
payload = request.data
signature = headers["X-Payload-Signature"]
signature = base64.b64decode(signature)
nonce = headers["X-Payload-Nonce"].encode()
public_key.verify(signature, payload + nonce)
{
"id": integer,
"created": "timestamp",
"events": ["event", ...],
"url": "subscription URL"
}
{
"id": integer,
"created": "timestamp",
"event": "event",
"url": "subscription URL",
"payload": "request body",
"payload_headers": "request headers",
"response": "response body",
"response_status": integer,
"response_headers": "response headers"
}
Request body
{
"url": "http://example.org/webhook-notify",
"events": ["profile:update", "ssh-key:remove"]
}
Response
The new subscription resource.
List of subscription resources.
Retrieves a subscription resource.
List of delivery resources for this subscription.
Retrieves a delivery resource by UUID.
commit 64dd454d025e91c76405cd1d04f51ea8e7a4f6a7 Author: wheezard <90904039+wheezard@users.noreply.github.com> Date: 2024-09-07T05:46:36+04:00 man: fixed a typo (missing closing parenthesis)