For developers and AI agents
Ruzora agent feeds
Open formats for agents that route hiring requests. Read whether we have vetted engineers for a skill, level and start date, then hand us the role as the schema.org JobPosting you already publish. No API key to read, and a verified work email to write.
Availability feed
Aggregated supply of the engineers we have already vetted: every one passed our coding test and AI technical interview and confirmed their availability in the last 30 days. The feed is cells of skill, seniority and start window. Refreshed from the live bench and cached for up to 15 minutes.
https://www.ruzora.com/api/public/availability.jsonJSON Schema: https://www.ruzora.com/api/public/availability.schema.json. Responses send Cache-Control: public, max-age=300 and allow any origin.
Privacy rules
- A cell is published only when at least 5 engineers fall in it. Smaller cells are left out, not rounded up.
- No totals of any kind: no bench size, no per-skill sums. A total next to the cells would let anyone subtract their way to a suppressed cell.
- No engineer counts at all. A cell says only that supply is available.
- No location. Beyond what our blind engineer profiles already show (skill, seniority, availability), the feed adds nothing about any engineer except whether a confirmed start date falls in the next 30 days, so it can't be combined with them to work out where someone lives.
- No names, profiles, employers, cities, rates, scores or per-engineer entries appear in the feed.
- The whole feed hides itself (published: false) while the bench is too thin to publish anything, exactly when our public engineer page does.
Cell fields
| Field | Meaning |
|---|---|
skill / skill_label | A technology from our skill taxonomy, as listed on the engineers' resumes. |
seniority | mid, senior, lead or staff. |
window | now (available today) or within_30_days (a confirmed start date in the next 30 days). |
supply | Always available. A cell only exists when it clears the privacy floor. |
A skill in this feed is what the engineers list, not what they were assessed on. Per-skill assessment results appear on the blind profiles in a shortlist.
Example (illustrative)
{
"$schema": "https://www.ruzora.com/api/public/availability.schema.json",
"schema_version": "1.0",
"published": true,
"as_of": "2026-10-14",
"min_cell_size": 5,
"counts": "none",
"cells": [
{
"skill": "python",
"skill_label": "Python",
"seniority": "senior",
"window": "now",
"supply": "available"
}
]
}Full JSON Schema
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://www.ruzora.com/api/public/availability.schema.json",
"title": "Ruzora vetted engineer availability",
"description": "Anonymized, aggregated supply of Ruzora's vetted engineers in Latin America. Every engineer counted passed Ruzora's coding test and AI technical interview and confirmed availability in the last 30 days. A cell is published only when at least min_cell_size engineers fall in it. There are no totals, counts, names, profiles, locations or per-engineer entries.",
"type": "object",
"additionalProperties": false,
"required": [
"schema_version",
"published",
"as_of",
"min_cell_size",
"counts",
"cells"
],
"properties": {
"$schema": {
"type": "string",
"format": "uri"
},
"schema_version": {
"type": "string",
"const": "1.0"
},
"published": {
"type": "boolean",
"description": "False while the bench is too thin to publish anything; cells is then empty."
},
"as_of": {
"type": "string",
"format": "date",
"description": "UTC date the data was read."
},
"min_cell_size": {
"type": "integer",
"minimum": 5,
"description": "k-anonymity floor: smaller cells are left out, not rounded."
},
"counts": {
"const": "none",
"description": "Always none: a cell only says supply is available. No engineer numbers are published."
},
"dimensions": {
"type": "object"
},
"docs": {
"type": "string",
"format": "uri"
},
"request_shortlist": {
"type": "object",
"properties": {
"intake": {
"type": "string",
"format": "uri"
},
"mcp": {
"type": "string",
"format": "uri"
},
"human": {
"type": "string",
"format": "uri"
}
}
},
"cells": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"skill",
"skill_label",
"seniority",
"window",
"supply"
],
"properties": {
"skill": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-]*$",
"description": "Skill taxonomy slug, as listed on the engineer's resume (not an assessment result)."
},
"skill_label": {
"type": "string"
},
"seniority": {
"enum": [
"mid",
"senior",
"lead",
"staff"
]
},
"window": {
"enum": [
"now",
"within_30_days"
],
"description": "now: available today. within_30_days: confirmed start date 1-30 days out."
},
"supply": {
"const": "available"
}
}
}
}
}
}JobPosting intake
Send the schema.org JobPosting for a remote engineering role and it becomes a vetted shortlist request, the same request our MCP tool request_vetted_shortlist makes. A vetted shortlist within 72 hours goes to the verified work email; profiles are blind until an agreement is signed.
https://api.ruzora.com/api/public/jobs/intake1. Verify the hiring manager’s work email
Writes need a buyer token for a verified work email. Personal inboxes such as Gmail are refused. Get one from the Ruzora MCP server at https://mcp.ruzora.com/mcp: call verify_work_email, which emails a 6-digit code, then confirm_work_email with that code. Confirming that code creates a Sol account for that email (or signs you into the one you already have), and the role and its shortlist appear in Sol at sol.ruzora.com. See your Sol account in our privacy policy. MCP install guide.
The token lasts 7 days and only ever acts for that email: the shortlist always goes to it, whatever the posting says. Treat it like a password. Anyone holding it can post roles for that email from anywhere until it expires, so keep it out of shared transcripts and logs.
2. Post the JobPosting
curl -X POST https://api.ruzora.com/api/public/jobs/intake \
-H "Authorization: Bearer $RUZORA_BUYER_TOKEN" \
-H "Content-Type: application/ld+json" \
--data @job-posting.json{
"@context": "https://schema.org",
"@type": "JobPosting",
"title": "Senior Backend Engineer",
"description": "<p>Own our payments API and its Postgres schema. Small team, weekly releases.</p>",
"skills": [
"Python",
"PostgreSQL",
"AWS"
],
"employmentType": "CONTRACTOR",
"jobLocationType": "TELECOMMUTE",
"applicantLocationRequirements": [
{
"@type": "Country",
"name": "Brazil"
}
],
"hiringOrganization": {
"@type": "Organization",
"name": "Acme",
"sameAs": "https://acme.example"
},
"baseSalary": {
"@type": "MonetaryAmount",
"currency": "USD",
"value": {
"@type": "QuantitativeValue",
"minValue": 90000,
"maxValue": 120000,
"unitText": "YEAR"
}
},
"totalJobOpenings": 1,
"applicationContact": {
"@type": "ContactPoint",
"name": "Casey Lee"
}
}The body can also be a list, or a document with an @graph, that contains one JobPosting. Up to 64 KB. The token can go in X-Ruzora-Buyer-Token instead of Authorization.
How fields are read
| Field | Required | What happens |
|---|---|---|
title | yes | The role. Seniority is read from it (Senior, Staff, Lead...). |
description | yes | Notes for the shortlist (HTML is stripped). It can't ask for a protected characteristic (age, sex, national origin, accent...). |
skills | yes | List must-haves first. The first 3 that name a technology in our skill taxonomy are hard requirements; every other skill (more technologies, soft skills, words we don't recognise) only ranks matches, up to 12. |
hiringOrganization.name | yes | The company. |
jobLocationType | TELECOMMUTE | Ruzora places remote engineers who live in Latin America. |
experienceRequirements.monthsOfExperience | if the title has no level | 36+ mid, 60+ senior, 96+ staff. Junior roles are declined. |
employmentType | no | Noted. INTERN and VOLUNTEER are declined. |
applicantLocationRequirements | no | Noted. A list without a Latin American country returns a warning, not an error. |
baseSalary | no | Noted as the budget. |
totalJobOpenings | no | Engineers wanted, 1 to 20. |
identifier / url | no | Noted. Two postings with the same title but different identifiers are two requests. |
validThrough | no | An expired posting is declined. |
applicationContact.name | no | Who we greet. Its email, if given, must be the verified one. |
3. Read the answer
{
"accepted": true,
"already_submitted": false,
"reference": "3f9c2a1b",
"shortlist_status": "preview",
"message": "Thanks Casey, here are your first matches. I'm sourcing a few more to round out your shortlist.",
"first_matches": [
{
"position": 1,
"headline": "Senior backend engineer",
"top_skills": [
"Python",
"PostgreSQL"
]
}
],
"next_steps": [
"A vetted shortlist within 72 hours goes to the verified work email. Profiles are blind until an agreement is signed."
],
"warnings": [],
"sol_app_url": "https://sol.ruzora.com/roles/…",
"link_emailed": true
}To meet anyone: pick who you want to meet, Roberto (founder) sets up a quick call, you sign the service agreement, and then interviews are scheduled with the engineers’ full identity.
Errors and limits
Every error is {"detail": {"code", "message", "docs", "next_step"}}. Validation errors list every field to fix in one response.
| Status | Code | Meaning |
|---|---|---|
| 201 | accepted | A new shortlist request. |
| 200 | already_submitted | The same posting (same title and identifier, or the same skills when there is no identifier) came in during the last 30 minutes. Same reference and Sol link; it never counts toward, or is refused by, the daily limit. |
| 400 | invalid_json / personal_email | Body is not JSON (or is nested too deeply), or the verified address is a personal inbox. |
| 401 | buyer_token_missing / _invalid / _expired / _revoked | Verify the work email again (next_step says how). |
| 413 | payload_too_large | Over 64 KB. |
| 422 | invalid_job_posting | errors[] names every field to fix and the next step for each. |
| 429 | rate_limited | Per connection (20 an hour), per work email (5 new requests a day) or per email domain (20 a day). Retry-After is set. |
| 202 | accepted | The request was saved but part of the processing failed. Same answer shape, with a warning; nothing to retry. |
| 500 | shortlist_failed | The request did not go through and the daily limit was not used. Retrying is safe: a retry within 30 minutes never creates a duplicate. |
| 503 | verification_unavailable | Verified actions are paused. Nothing was submitted. |
More for agents
- The Ruzora MCP server (hiring tools, work-email verification)
- llms.txt
- Not an agent? Request a shortlist
Canonical: https://www.ruzora.com/developers/feeds
