Skip to content

HTTP

Versia uses the HTTP protocol for all communications between instances.

Signatures lists the routes where requests and responses are signed.

TLS

Requests not encrypted with TLS (HTTPS) MUST NOT be sent or responded to. All implementations MUST use TLS 1.2 or higher, and MUST support HTTP/2.

Requests

Headers

Accept string

MUST be application/vnd.versia+json on every /.versia/ route except the inbox and Instance Messaging, which do not need it. Basic application/json MUST NOT be used on those routes. The well-known and WebFinger routes state their own.

Content-Type string Required

MUST be application/vnd.versia+json if the request has a body, except on the Instance Messaging route. Receivers MUST accept it with or without charset=utf-8.

Versia-Signature string Required

See Signatures for more information.

Versia-Signed-At number Required
User-Agent string

A string identifying the software making the request. SHOULD contain the name of the software, its version, and a link to its homepage.

CoolServer/1.0 (https://coolserver.example)
POST /.versia/v0.7/inbox HTTP/1.1
Host: bob.example
Content-Type: application/vnd.versia+json
User-Agent: CoolServer/1.0 (https://coolserver.example)
Versia-Signature: RW7hr/xNzshs446OAPnPxR1vh7ZfZH+FDfn59bLc3LD3SIeR7udR2NqsMLfb4IQvhoFRWE1IL3sdQTPPVaDwCg==
Versia-Signed-By: example.com
Versia-Signed-At: 1729241687

{"type":"Delete","created_at":"2024-10-18T08:54:47Z","author":"example.com:033wv9k6n0007","deleted_type":"pub.versia:social/Note","deleted":"example.com:033qtdbdkj006"}

Encoding

Entity types, extension names and collection names go in paths as they are, so a namespaced type spans two path segments. They MUST NOT be percent-encoded, so that each path has one spelling for signatures to cover. A receiver does not have to accept an encoded form.

/.versia/v0.7/entities/pub.versia:groups/Group/1234
/.versia/v0.7/entities/User/1234/collections/pub.versia:likes/Likes

Redirects

API endpoints MUST NOT redirect to other endpoints, with the following exceptions:

  • When a GET request does not have an Accept: application/vnd.versia+json header, the server MAY redirect to an HTML representation of the resource.

API endpoints MUST NOT redirect from HTTP to HTTPS. This is to force all API communication to be encrypted, and to avoid accidentally leaking sensitive data over unencrypted connections.

This is forbidden:

GET /.versia/v0.7/entities/User/1234 HTTP/1.1
Host: example.com
Accept: application/vnd.versia+json

HTTP/1.1 301 Moved Permanently
Location: https://example.com/users/1234

This is allowed:

GET /.versia/v0.7/entities/User/1234 HTTP/1.1
Host: example.com
Accept: text/html

HTTP/1.1 301 Moved Permanently
Location: https://example.com/users/1234

Rate Limits

An instance that rate limits a request MUST respond with a 429 Too Many Requests status code and a Retry-After header. The sender MUST NOT retry the request before the time that header gives.

Retries

A sender delivering to an inbox SHOULD retry with increasing delays after a network failure, a 5xx status code, a 408 Request Timeout, a 429 Too Many Requests, or a 2xx response it had to discard. It MUST honor any Retry-After header. It MUST NOT retry after any other 4xx status code.

Each retry MUST be signed again, as the signature’s timestamp will no longer be valid.

How long to keep trying is up to the implementation. 48 hours is a reasonable default.

Responses

Headers

Content-Type string Required

MUST be application/vnd.versia+json if the response has a body, unless specified otherwise. Receivers MUST accept it with or without charset=utf-8.

Versia-Signature string Required

See Signatures for more information.

Versia-Signed-At number Required
# Answers GET /.versia/v0.7/instance/collections/admins, sent to bob.example
HTTP/1.1 200 OK
Content-Type: application/vnd.versia+json
Versia-Signature: 9Xkg1gy82HxAoXeXk7je0Qc9kiBWXuVRmqXXdvBUW+z3USKzCkfGrttFHkqBALPlexghmiXzMzxM98kURplxAA==
Versia-Signed-By: bob.example
Versia-Signed-At: 1729241717

{"total":0,"items":[]}

Status Codes

Every federation route uses these status codes. An inbox that answers 2xx before it has finished checking an entity cannot report a problem found later, so it drops the entity.

Status codeMeaning
200 OKThe requested entity or collection is in the body.
202 AcceptedThe inbox took the entity. A repeated signature and an unknown extension type also get a 2xx status code.
400 Bad RequestThe body is not valid JSON.
401 UnauthorizedThe signature is missing or invalid, or its timestamp is too old or too new.
403 ForbiddenThe receiver refuses to federate with the sender’s instance.
404 Not FoundThere is no such entity, or the requesting instance may not fetch it.
410 GoneThe entity was deleted.
415 Unsupported Media TypeThe Content-Type is wrong.
422 Unprocessable EntityThe entity is invalid, its author does not belong to the signer, or its core type is unknown. Also used for invalid pagination parameters.
429 Too Many RequestsThe sender is rate limited.

The inbox SHOULD answer with 202 Accepted and no body. Senders MUST treat any 2xx status code from the inbox as delivered, as long as its signature verifies.

Error Bodies

A response with a 4xx or 5xx status code MAY have a body in the format below. The status code is what counts: an instance MUST NOT change its behavior based on an error body.

Fields

error string Required

Short description of the problem.

Longer description of the problem, for humans.

details object

Problems by field. Keys are paths to the field, and values are arrays of strings describing what is wrong with it.

{
    "error": "Validation failed",
    "error_description": "The entity does not match its definition.",
    "details": {
        "created_at": ["Must be an RFC 3339 timestamp"]
    }
}