> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tsenta.com/llms.txt
> Use this file to discover all available pages before exploring further.

# API reference

> Endpoints, the application object, statuses, failure reasons, review, webhooks, verification codes and errors for the Tsenta application API.

Base URL `https://api.autojobs.me/v1`. Every request carries
`Authorization: Bearer sk_live_...`.

## Endpoints

| Method | Path                        | Purpose                                            |
| ------ | --------------------------- | -------------------------------------------------- |
| `GET`  | `/ats`                      | Supported ATS catalog.                             |
| `POST` | `/detect`                   | Identify the system behind a posting URL.          |
| `POST` | `/candidates`               | Create a candidate from a profile and resume.      |
| `POST` | `/profiles`                 | Save a reusable profile for an existing candidate. |
| `GET`  | `/profiles`                 | List saved profiles.                               |
| `GET`  | `/profiles/{id}`            | One profile plus its application runs.             |
| `POST` | `/applications`             | Apply. Spends credit and queues the work.          |
| `GET`  | `/applications`             | List applications, newest first.                   |
| `GET`  | `/applications/{id}`        | One application.                                   |
| `POST` | `/applications/{id}/review` | Approve or reject an application parked on review. |
| `POST` | `/applications/{id}/otp`    | Supply a verification code the candidate received. |
| `GET`  | `/usage`                    | Counts and spend.                                  |

## The application object

One shape, returned by create, fetch, list and review. Money fields end in
`_usd` and are numbers.

```json theme={null}
{
  "id": "9f1c8a2e",
  "candidate_id": "cand_8f2a",
  "candidate_name": "Ada Lovelace",
  "candidate_email": "ada@example.com",
  "profile_id": "prof_4c1d",
  "ats": "lever",
  "url": "https://jobs.lever.co/acme/123/apply",
  "status": "queued",
  "failure_reason": null,
  "price_usd": 0.09,
  "review": null,
  "created_at": "2026-08-06T12:00:00.000Z",
  "updated_at": "2026-08-06T12:00:04.000Z"
}
```

`failure_reason` is null unless the status is `failed`. `review` is null unless
the status is `needs_review`. `ats` is null when the posting is not one of the
systems in the catalog.

## Statuses

| Status         | Meaning                                              |
| -------------- | ---------------------------------------------------- |
| `queued`       | Accepted, not started. Credit is held.               |
| `running`      | In progress.                                         |
| `needs_review` | Paused. Answers are waiting on your decision.        |
| `submitted`    | Terminal, success. This is what you are charged for. |
| `failed`       | Terminal. Not charged.                               |

## Failure reasons

| Reason                       | Meaning                                              |
| ---------------------------- | ---------------------------------------------------- |
| `job_closed`                 | The posting no longer accepts applications.          |
| `unsupported_site`           | The posting is not on a supported system.            |
| `incomplete_candidate`       | The profile was missing something the form required. |
| `manual_completion_required` | The application needs a person.                      |
| `rejected_at_review`         | You rejected it at the review step.                  |
| `site_error`                 | The site failed or behaved unexpectedly.             |

## Errors

Every failure, on every endpoint, uses the same envelope.

```json theme={null}
{
  "error": {
    "code": "insufficient_credit",
    "message": "Your balance does not cover this application."
  }
}
```

| Code                    | HTTP            | Meaning                                                                |
| ----------------------- | --------------- | ---------------------------------------------------------------------- |
| `unauthorized`          | 401             | Missing, malformed, or revoked key.                                    |
| `invalid_request`       | 400 / 413 / 422 | Malformed JSON, an oversized body, or a request that did not validate. |
| `not_found`             | 404             | No such resource on your account.                                      |
| `insufficient_credit`   | 402             | Balance does not cover the application.                                |
| `rate_limited`          | 429             | Too many requests too fast.                                            |
| `duplicate_application` | 409             | This candidate already applied to this URL.                            |
| `invalid_state`         | 409             | The application is not in a state that allows this.                    |
| `internal_error`        | 500             | Something broke on our side. Check state before retrying.              |

## Review before submit

Set `review_before_submit` on an application and it pauses at the final step
instead of submitting. The application moves to `needs_review` and exposes a
`review` object holding the questions waiting on you.

Question kinds: `text`, `textarea`, `email`, `phone`, `url`, `single_select`,
`multi_select`, `radio`, `checkbox`, `boolean`, `date`, `file`, `cover_letter`,
`typeahead`, `hidden`, `display`.

```bash theme={null}
curl -sX POST https://api.autojobs.me/v1/applications/9f1c/review \
  -H "Authorization: Bearer $TSENTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "approve", "answers": { "years_experience": "6" } }'
```

Rejecting settles the application as `failed` with `rejected_at_review` and
releases the held credit.

## Webhooks

Subscribe once and stop polling.

| Event                      | Fires when                        |
| -------------------------- | --------------------------------- |
| `application.running`      | Work has started.                 |
| `application.needs_review` | Paused, waiting on your decision. |
| `application.needs_otp`    | A verification code is needed.    |
| `application.submitted`    | Terminal, success.                |
| `application.failed`       | Terminal, not charged.            |

Delivery is signed and at-least-once with a dedupe key, so make your handler
idempotent. Redirects are rejected rather than followed.

## Candidate email

Chosen per candidate with `email_mode`.

<AccordionGroup>
  <Accordion title="managed (default)">
    A Tsenta-controlled address. This is what makes systems that email a
    verification code work at all: the code lands somewhere we can read. Employer
    replies land there too rather than with the candidate.
  </Accordion>

  <Accordion title="candidate">
    The candidate's own address from their profile. They receive everything
    directly, but we cannot read their inbox, so an application that needs a
    verification code will wait for you to supply it via
    `POST /applications/{id}/otp`.
  </Accordion>
</AccordionGroup>

## Workday

Workday creates a real account per employer and authenticates with it on later
applications, so it needs a password. Supply `workday_password` (8 to 128
characters) when creating the candidate. It is stored encrypted, never returned,
and never logged.

Applying to a Workday posting for a candidate created without one returns `422`
`invalid_request`. No other system needs this.
