toma / a useful reference
API reference
Documentation for the Toma API: open jobs, company teams, job posting, API keys, the MCP endpoint, the health check, and the OpenAPI contract.
read as Markdown · agent documentation index
Base URL
Use the same origin as this documentation, over HTTPS on the public deployment. All routes are under /api. Responses are JSON with Cache-Control: no-store. No SDK is required.
GET /api/health
Returns HTTP 200 with Content-Type: application/json and body {"status":"ok"}. Responses use Cache-Control: no-store. No parameters, request body, or authentication are required.
HEAD /api/health
Returns HTTP 200 with the same response headers and no body.
Authentication
REST: a human signs in on the website and creates an API key under company settings. Send it as Authorization: Bearer <key>. Keys start with tl_ and act as that human inside their team. Each key allows 120 requests per minute. Browser sessions also work for same-origin JSON requests. MCP does not accept API keys: MCP clients sign in with OAuth 2.1 (authorization code with PKCE, dynamic client registration). Discovery: /.well-known/oauth-protected-resource/api/mcp and /.well-known/oauth-authorization-server.
GET /api/jobs
Public. Returns {"jobs":[...]} with open jobs, newest first. Each job has id, company, title, budget in whole US dollars, constraints, status, and createdAt. Optional query: limit, from 1 to 200, default 100.
GET /api/jobs/{id}
Public. Returns one open job, or 404 when the id is unknown or the job is closed. Same as the get_job MCP tool. Opening a job counts as a view in its company's performance report.
GET /api/team/performance
Company accounts. Query: days (7, 30, or 90; default 30). Returns totals for the range and for the equal period before it (impressions, views, viewRate, applications, applyRate, agentShare), daily totals split by agents and people, one row per job with dailyViews, and the definitions used. An impression is a job returned in a list or search result; a view is a job's details opened. Each viewer counts once per job per UTC day; the team's own members and search crawlers are excluded. Same as the get_job_performance MCP tool.
GET /api/jobs/search
Public. Finds open jobs by meaning and keywords. Query: q (plain English, up to 300 characters, may include a budget or ordering such as "research under $200" or "highest paying design"), min_budget and max_budget (inclusive whole dollars), sort (relevance, newest, budget_desc, budget_asc), and limit (1 to 50, default 20). Explicit parameters override what q implies. Returns {"jobs":[...],"interpretation":{"query","minBudget","maxBudget","sort","interpreted"},"mode"}. mode is hybrid (semantic and full-text index), keyword (word-match fallback), or filter (no topic, filters only). The search_jobs MCP tool takes the same arguments, with query in place of q.
GET and PATCH /api/team
GET returns the caller's team, role, members, and jobs. PATCH with {"name":"..."} renames the company and needs the owner or admin role. Errors return {"error":"...","fields":{...}} with HTTP 400, 401, or 403.
GET and POST /api/team/jobs
GET lists the team's jobs. POST with {"title","budget","constraints","acceptListingFee":true} publishes an open job and returns HTTP 201. Every posting costs a $1 listing fee; acceptListingFee must be true or the request fails with HTTP 400. The fee is recorded, not charged, until payments are available. Titles are one line, all lowercase, and at most 120 characters. Budgets are whole dollars from 1 to 1,000,000. Constraints are at most 4,000 characters.
Candidate routes
Candidate accounts only. GET /api/candidate returns the profile and verification status. PUT /api/candidate/profile saves the profile; the state must be a US state or DC, and usWorkAuthorized must be true. GET /api/candidate/verification returns the background check and employment verification status.
Background checks are not available yet: the status is "unavailable" and POST /api/candidate/verification returns HTTP 503. When a screening provider is connected, the check will start only from the candidate's own browser session, because it records their consent.
Applications
Candidates: POST /api/jobs/{id}/applications with {"pitch","agent"} applies to an open job (HTTP 201). It needs a saved profile and allows one application per job. GET /api/candidate/applications lists your applications. POST /api/candidate/applications/{id}/withdraw withdraws one that is still waiting.
Companies: GET /api/team/jobs/{id}/applications lists a job's applicants with their profile, verification status, pitch, and agent. POST /api/team/applications/{id}/accept hires one applicant: the job is filled and leaves the board and search, other waiting applicants are declined, and both sides receive each other's email. POST /api/team/applications/{id}/decline declines one. POST /api/team/jobs/{id}/close stops taking applications without hiring. Emails are shared only after acceptance. If two people accept at once, one succeeds and the other gets HTTP 409.
POST /api/mcp
The MCP endpoint. Send one JSON-RPC 2.0 message per POST. Supports initialize, ping, tools/list, and tools/call. The tools mirror the REST operations and use the same validation.
Errors and supported methods
GET /api/health returns {"status":"ok"}, and HEAD returns the same headers with no body. GET /api/health/ready checks the database (200 or 503) and reports which sign-in methods, search mode, and background checks are configured. Unsupported methods return HTTP 405 with an Allow header. Unknown API paths return HTTP 404. Missing or invalid keys return HTTP 401. GET /api/jobs/search allows 30 searches per minute per caller and returns HTTP 429 with Retry-After beyond that. Unexpected errors return HTTP 500 with {"error","requestId"}; every API response carries an x-request-id header to quote to support@toma.com.
Example request
curl --fail --silent --show-error "$TOMA_URL/api/jobs?limit=10"
curl --fail --silent --show-error --get "$TOMA_URL/api/jobs/search" --data-urlencode "q=data cleanup under $300"
curl -X POST "$TOMA_URL/api/team/jobs" -H "Authorization: Bearer $TOMA_KEY" -H "Content-Type: application/json" -d '{"title":"audit our docs","budget":200,"constraints":"One week.","acceptListingFee":true}'
Set TOMA_URL to the public origin, without a trailing slash.