// API
API Reference
Affiro offers a minimal API at time of writing.
Authentication
Unless otherwise specified, endpoints work with or without a credential. An anonymous upload is stored and analyzed, but belongs to nobody and reaches no history page. Present an API key and the document is stored against you, with your default document settings applied. Present a publishable key and it is stored against the organization that issued the key instead of a person.
Create an API key on your API keys page. The token is shown once, when you create it, and no later request can return it. A key does not expire; revoke it from the same page when you are done with it or if it leaks. Present it as a bearer token, substituting the key you were shown for $AFFIRO_KEY:
curl -X POST https://app.affiro.com/api/v1/documents \
-H "Authorization: Bearer $AFFIRO_KEY" \
--data-raw '1.5rGhagAAAAA..Bgc
This is my document text!'
If you run that without setting AFFIRO_KEY it will still succeed. An unresolved token is simply anonymous on this endpoint, but the document will belong to nobody and will not appear in your history. Check the id comes back, then look for it on your documents page.
A token that does not resolve is treated the same way whether it is malformed, unknown, revoked, or expired, so a caller cannot tell which it holds. On an endpoint that requires a key the answer is:
HTTP/1.1 401 Unauthorized
{"error":"access denied"}
A key acts as you on POST /api/v1/documents and POST /api/v1/documents/{id}. Everywhere else it is nobody: it cannot read or change your settings, and it cannot create or revoke a key. POST, GET and DELETE /api/v1/api-keys take a browser session only, and answer a key token with the same 401 above.
Collecting documents into an organization
An API key acts as you, so it has to stay secret. A publishable key acts as an organization and goes in the source of a web page, where any visitor can read it. Use one to collect documents from a form you run, like a job application, when the people filling it in don't have accounts here.
Organization admins create publishable keys on the organization's page. You see the key once, when you create it, so copy it into your page then. Lost it? Revoke it and create a new one. Revoking a key doesn't remove the documents it already collected.
A publishable key works on one endpoint, POST /api/v1/documents, and only adds documents to its organization. It can't read documents, add versions, see settings, or create keys. Any other endpoint answers it with the 401 above.
curl -X POST https://app.affiro.com/api/v1/documents?externalId=applicant-1234 \
-H "Authorization: Bearer $AFFIRO_PUBLISHABLE_KEY" \
--data-raw '1.5rGhagAAAAA..Bgc
This is my application!'
{
"id":"sdc-1111111111111111",
"documentId":"doc-1111111111111111",
"externalId":"applicant-1234"
}
The response has no verdict. GET /api/v1/documents/{id}/metadata leaves it out too for documents collected this way. Admins read verdicts on the organization's page, which lists every document the organization's keys have collected. Nobody else can open those documents.
externalId is optional. Set it to whatever you use to find the document later, such as an applicant number. It must be unique within your organization, and a repeat gets a 409:
HTTP/1.1 409 Conflict
{"error":"conflict: that externalId is already used by another document here"}
Anyone who has the key can upload with it. Revoke any key you're not actively using.
GET /api/v1/documents/{id}/metadata will retrieve public metadata for a document:
curl https://app.affiro.com/api/v1/documents/my-document-id/metadata
{
"id":"sdc-bb35a208b3b096cb",
"documentId":"doc-c901be933edce201",
"externalId":"my-document-id",
"version":1,
"signature":"1.5rGhagAAAAA..Bgc",
"human_written":"unknown",
"created_at":"2026-09-09T19:22:17.856809Z"
}
POST /api/v1/documents/ will upload a document and signature pair:
curl -X POST https://app.affiro.com/api/v1/documents \
--data-raw '1.5rGhagAAAAA..Bgc
This is my document text!'
{
"id":"sdc-1111111111111111",
"documentId":"doc-1111111111111111",
"externalId":"",
"humanWritten":"very unlikely",
"descriptions":[
"typing pattern strongly resembles scripted input",
"contains no pastes",
"typing included no pauses"
]
}
POST /api/v1/documents/{id}/ will upload a new version of a document and signature pair:
curl -X POST https://app.affiro.com/api/v1/documents/my-external-id \
--data-raw '1.5rGhagAAAAA..Bgc
This is my new document text!'
{
"id":"sdc-2222222222222222",
"documentId":"doc-1111111111111111",
"externalId":"my-external-id",
"humanWritten":"very unlikely",
"descriptions":[
"typing pattern strongly resembles scripted input",
"contains no pastes",
"typing included no pauses"
]
}
A new version is stored against you if you created the document, and inherits the previous creator if you present no credential. Versioning a document somebody else created is refused:
HTTP/1.1 400 Bad Request
{"error":"document changed creators"}