Reference
Company
Authorise a company by its domain, give out its addresses and run its admins.
Calls go to https://address.seleya.ai/api. A refusal is {"error": {"code", "message"}} with its status; every code is in Errors.
Claim your own company address
POST /v1/addresses/self
Called with a key or a person signed in.
A person (or their app) whose email is at an authorised company's domain claims their own address there.
Body
-
ownerstring - The company, when several are at your email's domain.
-
localstring - The name before the @. Defaults to your email's.
-
display_namestring - The name people see. It must say it is an agent.
Answers
- 201
- Made
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 409
- Conflicts with the current state
- 422
- A value is refused; the message says which
Fields of the 201 answer
-
idstring -
addressstring -
owner_idstring -
display_namestring -
statusactive | retired -
holder_emailsarray of strings -
holder_person_idstring, or null -
screeningmap of string -
capabilitiesCapabilities -
approvalsoff | unknown_recipients | everything
curl -X POST https://address.seleya.ai/api/v1/addresses/self \
-H "Authorization: Bearer $SELEYA_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Ada'\''s agent"
}'import os, requests
r = requests.post(
"https://address.seleya.ai/api/v1/addresses/self",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
json={
"display_name": "Ada's agent"
},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/addresses/self", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"display_name": "Ada's agent"
}),
});
console.log(r.status, await r.json());{
"id": "7f3c9a2e-0000-4000-8000-000000000001",
"address": "desk@acme.seleya.email",
"owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
"display_name": "Desk",
"status": "active",
"holder_emails": [
"ada@acme.example"
],
"holder_person_id": "7f3c9a2e-0000-4000-8000-000000000003",
"screening": {}
}Change the holder
PUT /v1/addresses/{a}/holder
Called with a key or a person signed in.
Moves the address to another person. The address and its history stay. From a key or app it waits for the company's admins.
Parameters
-
astring, in path required - The address, such as desk@acme.seleya.email.
Body
-
emailstring required - The new holder's email address. They get an email saying so.
Answers
- 200
- Done
- 202
- Waits for a person, who was emailed a link
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 404
- Not found, or not yours to see
- 422
- A value is refused; the message says which
- 502
- Done, but an email could not be sent
Fields of the 200 answer
-
idstring -
addressstring -
owner_idstring -
display_namestring -
statusactive | retired -
holder_emailsarray of strings -
holder_person_idstring, or null -
screeningmap of string -
capabilitiesCapabilities -
approvalsoff | unknown_recipients | everything
curl -X PUT https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/holder \
-H "Authorization: Bearer $SELEYA_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "lee@acme.com"
}'import os, requests
r = requests.put(
"https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/holder",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
json={
"email": "lee@acme.com"
},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/holder", {
method: "PUT",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"email": "lee@acme.com"
}),
});
console.log(r.status, await r.json());{
"id": "7f3c9a2e-0000-4000-8000-000000000001",
"address": "desk@acme.seleya.email",
"owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
"display_name": "Desk",
"status": "active",
"holder_emails": [
"ada@acme.example"
],
"holder_person_id": "7f3c9a2e-0000-4000-8000-000000000003",
"screening": {}
}Retire an address
POST /v1/addresses/{a}/retire
Called with a key or a person signed in.
Retires an address for good: it stops receiving and sending, its keys end, and its name is never given out again. From a key or app it waits for the admins.
Parameters
-
astring, in path required - The address, such as desk@acme.seleya.email.
Answers
- 200
- Done
- 202
- Waits for a person, who was emailed a link
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 404
- Not found, or not yours to see
Fields of the 200 answer
-
idstring -
addressstring -
owner_idstring -
display_namestring -
statusactive | retired -
holder_emailsarray of strings -
holder_person_idstring, or null -
screeningmap of string -
capabilitiesCapabilities -
approvalsoff | unknown_recipients | everything
curl -X POST https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/retire \
-H "Authorization: Bearer $SELEYA_KEY"import os, requests
r = requests.post(
"https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/retire",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/retire", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());{
"address": "second@acme.seleya.email",
"display_name": "Second agent",
"holder_emails": [],
"holder_person_id": null,
"id": "7f3c9a2e-0000-4000-8000-00000000002a",
"owner_id": "7f3c9a2e-0000-4000-8000-000000000020",
"screening": {},
"status": "retired"
}Start authorising a company
POST /v1/companies
Called with a key or a person signed in.
Starts authorising a company: by a DNS record, or by the company's own mail records for agents.<domain>. The legal name and country are screened against sanctions lists.
Body
-
namestring required - The company's legal name. It is screened against sanctions lists.
-
domainstring required - The domain its people's email is on, not a subdomain.
-
countrystring required - Where it is registered, as a two-letter code such as ZA.
-
methoddns | mail - dns: one text record we give you. mail: the mail records for agents.<domain>, which also prove the domain. Defaults to dns.
Answers
- 201
- Made
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 409
- Conflicts with the current state
- 422
- A value is refused; the message says which
- 429
- A limit is reached
Fields of the 201 answer
-
idstring -
namestring -
domainstring -
methoddns | mail -
statusstring -
countrystring -
recordsobject -
checkobject -
errorobject -
companyobject -
created_atstring
curl -X POST https://address.seleya.ai/api/v1/companies \
-H "Authorization: Bearer $SELEYA_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Robotics Ltd",
"domain": "acme.com",
"country": "ZA",
"method": "dns"
}'import os, requests
r = requests.post(
"https://address.seleya.ai/api/v1/companies",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
json={
"name": "Acme Robotics Ltd",
"domain": "acme.com",
"country": "ZA",
"method": "dns"
},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/companies", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"name": "Acme Robotics Ltd",
"domain": "acme.com",
"country": "ZA",
"method": "dns"
}),
});
console.log(r.status, await r.json());{
"check": null,
"company": null,
"country": "ZA",
"created_at": "2026-10-22T00:02:08.456527+00:00",
"domain": "beta.com",
"error": null,
"id": "7f3c9a2e-0000-4000-8000-000000000021",
"method": "dns",
"name": "Pay Co 10220001",
"own_domain": "",
"records": [
{
"name": "_beta.com",
"type": "TXT",
"value": "seleya-address-verify=30G_rSH4qrMhjsufRafp1550yWlpbQon"
}
],
"status": "pending",
"subdomain": "acme.seleya.email"
}Your authorisations
GET /v1/companies/authorisations
Called with a key or a person signed in.
The company authorisations you started, open and done.
Answers
- 200
- Done
- 401
- No valid key, session or token
Fields of the 200 answer
-
authorisationsarray of Authorisation
curl https://address.seleya.ai/api/v1/companies/authorisations \
-H "Authorization: Bearer $SELEYA_KEY"import os, requests
r = requests.get(
"https://address.seleya.ai/api/v1/companies/authorisations",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/companies/authorisations", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());{
"authorisations": []
}One authorisation
GET /v1/companies/authorisations/{id}
Called with a key or a person signed in.
One authorisation: the records to publish, the last check, and the company once it is set up.
Parameters
-
idstring, in path required - Its id.
Answers
- 200
- Done
- 401
- No valid key, session or token
- 404
- Not found, or not yours to see
Fields of the 200 answer
-
idstring -
namestring -
domainstring -
methoddns | mail -
statusstring -
countrystring -
recordsobject -
checkobject -
errorobject -
companyobject -
created_atstring
curl https://address.seleya.ai/api/v1/companies/authorisations/ID \
-H "Authorization: Bearer $SELEYA_KEY"import os, requests
r = requests.get(
"https://address.seleya.ai/api/v1/companies/authorisations/ID",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/companies/authorisations/ID", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());{
"check": "no text record at _beta.com yet; DNS can take a few minutes",
"company": null,
"country": "ZA",
"created_at": "2026-10-22T00:02:08.456527+00:00",
"domain": "beta.com",
"error": null,
"id": "7f3c9a2e-0000-4000-8000-000000000021",
"method": "dns",
"name": "Pay Co 10220001",
"own_domain": "",
"records": [
{
"name": "_beta.com",
"type": "TXT",
"value": "seleya-address-verify=30G_rSH4qrMhjsufRafp1550yWlpbQon"
}
],
"status": "pending"
}Check its proof
POST /v1/companies/authorisations/{id}/check
Called with a key or a person signed in.
Checks the proof now.
Parameters
-
idstring, in path required - Its id.
Answers
- 200
- Done
- 401
- No valid key, session or token
- 404
- Not found, or not yours to see
- 409
- Conflicts with the current state
Fields of the 200 answer
-
idstring -
namestring -
domainstring -
methoddns | mail -
statusstring -
countrystring -
recordsobject -
checkobject -
errorobject -
companyobject -
created_atstring
curl -X POST https://address.seleya.ai/api/v1/companies/authorisations/ID/check \
-H "Authorization: Bearer $SELEYA_KEY"import os, requests
r = requests.post(
"https://address.seleya.ai/api/v1/companies/authorisations/ID/check",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/companies/authorisations/ID/check", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());{
"check": "no text record at _beta.com yet; DNS can take a few minutes",
"company": null,
"country": "ZA",
"created_at": "2026-10-22T00:02:08.456527+00:00",
"domain": "beta.com",
"error": null,
"id": "7f3c9a2e-0000-4000-8000-000000000021",
"method": "dns",
"name": "Pay Co 10220001",
"own_domain": "",
"records": [
{
"name": "_beta.com",
"type": "TXT",
"value": "seleya-address-verify=30G_rSH4qrMhjsufRafp1550yWlpbQon"
}
],
"status": "pending"
}A company
GET /v1/companies/{o}
Called with a key or a person signed in.
A company, for its admins: its domains, status, addresses and admins.
Parameters
-
ostring, in path required - The company's owner id.
Answers
- 200
- Done
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 404
- Not found, or not yours to see
Fields of the 200 answer
-
owner_idstring -
namestring -
proven_domainstring -
mail_domainstring -
own_domainstring -
address_domainsarray of strings -
statusstring -
addressesinteger -
membersarray of objects -
capabilitiesCapabilities
curl https://address.seleya.ai/api/v1/companies/OWNER_ID \
-H "Authorization: Bearer $SELEYA_KEY"import os, requests
r = requests.get(
"https://address.seleya.ai/api/v1/companies/OWNER_ID",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/companies/OWNER_ID", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());{
"owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
"name": "Acme",
"proven_domain": "acme.example",
"mail_domain": "acme.seleya.email",
"own_domain": "",
"address_domains": [
"acme.seleya.email"
],
"status": "active",
"addresses": 1,
"members": [
{
"person_id": "7f3c9a2e-0000-4000-8000-000000000003",
"email": "ada@acme.example",
"role": "admin"
}
]
}Add an admin
POST /v1/companies/{o}/members
Called with a key or a person signed in.
Adds an admin. It grants authority, so from a key or app it waits for the admins.
Parameters
-
ostring, in path required - The company's owner id.
Body
-
emailstring required - The new admin's email address. They get an email saying so.
Answers
- 201
- Made
- 202
- Waits for a person, who was emailed a link
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 404
- Not found, or not yours to see
- 422
- A value is refused; the message says which
- 502
- Done, but an email could not be sent
Fields of the 202 answer
-
statusawaiting_approval - awaiting_approval.
-
approvalobject - The ask: its id, kind and who decides.
curl -X POST https://address.seleya.ai/api/v1/companies/OWNER_ID/members \
-H "Authorization: Bearer $SELEYA_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "lee@acme.com"
}'import os, requests
r = requests.post(
"https://address.seleya.ai/api/v1/companies/OWNER_ID/members",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
json={
"email": "lee@acme.com"
},
)
print(r.status_code, r.json())const r = await fetch("https://address.seleya.ai/api/v1/companies/OWNER_ID/members", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"email": "lee@acme.com"
}),
});
console.log(r.status, await r.json());{
"person_id": "7f3c9a2e-0000-4000-8000-000000000003",
"email": "ada@acme.example",
"role": "admin"
}Remove an admin
DELETE /v1/companies/{o}/members/{p}
Called with a key or a person signed in.
Removes an admin. From a key or app it waits for the admins.
Parameters
-
ostring, in path required - The company's owner id.
-
pstring, in path required - The admin's person id.
Answers
- 202
- Waits for a person, who was emailed a link
- 204
- Done, with no body
- 401
- No valid key, session or token
- 403
- Not allowed for this caller
- 404
- Not found, or not yours to see
- 409
- Conflicts with the current state
Fields of the 202 answer
-
statusawaiting_approval - awaiting_approval.
-
approvalobject - The ask: its id, kind and who decides.
curl -X DELETE https://address.seleya.ai/api/v1/companies/OWNER_ID/members/PERSON_ID \
-H "Authorization: Bearer $SELEYA_KEY"import os, requests
r = requests.delete(
"https://address.seleya.ai/api/v1/companies/OWNER_ID/members/PERSON_ID",
headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.text)const r = await fetch("https://address.seleya.ai/api/v1/companies/OWNER_ID/members/PERSON_ID", {
method: "DELETE",
headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status);{
"status": "awaiting_approval",
"approval": {
"id": "7f3c9a2e-0000-4000-8000-000000000001",
"kind": "remove_admin",
"summary": "remove ada@acme.example as admin",
"waits_for": "a person, who was sent a link by email"
}
}