# Create a test submission in a sandbox (https://terac.com/docs/developers/reference/createTestSubmission)

`POST https://terac.com/api/external/v2/opportunities/{opportunityId}/test-submissions`

Creates a test submission in a sandbox organization, so your webhook endpoint receives the events a real participant would trigger.

Terac adds a test participant and moves it through the opportunity a few seconds at a time: it answers
the screener, starts the task and finishes it. Each step sends the same `submission.status.change`
webhook a real participant's step sends, signed and retried the same way. Nobody is contacted and
nobody is paid. Only a subscription created with a key from the same sandbox organization receives
these webhooks.

The participant stops wherever the next move is yours: inviting or declining an applicant, approving
or rejecting the work, or your own completion redirect when `task_completion` is `redirect`. Your call
sends the next webhook. After you invite an applicant, the participant carries on with the task.

The test submission shows in the sandbox dashboard under the name "Test Participant" and counts in the
opportunity's numbers. It does not count toward a quota, and it cannot be removed.

Returns 403 outside a sandbox organization, and 409 when the opportunity is not active. Returns 422
when Terac cannot create the test submission you asked for on this opportunity, and the `message`
says why: for example `failed` with no screener, `passed` with screening questions that no answer
passes, or `redirect` when the first task has no `task_url`.

## Authorization

Requires a Bearer token via the `Authorization` header.

```
Authorization: Bearer <token>
```

## Path Parameters

- `opportunityId` (string) **(required)**: ID of the opportunity, as returned by `POST /opportunities` or `GET /opportunities`.

## Request Body (required)

`Content-Type: application/json`

- `screening_outcome` (string): one of: `passed`, `failed`, `filtered_out`: How screening goes for the test participant. "passed" (the default) gets through the screener. "failed" answers the screener and is screened out. "filtered_out" is screened out on its profile before the screener, and is allowed even when the opportunity has no filters.
- `task_completion` (string): one of: `automatic`, `redirect`: Who finishes the task, for a test participant that reaches it. "automatic" (the default): Terac finishes it. "redirect": Terac starts the task and stops, and you finish it by opening `task_url` and letting your own completion redirect bring the participant back to Terac.

## Responses

### 200: The test submission, which a test participant is now taking through the opportunity.

- `id` (string) **(required)**: ID of the test submission. `GET /submissions/{submissionId}` returns 404 while the test participant is still in screening.
- `opportunity_id` (string) **(required)**
- `task_url` (string, nullable) **(required)**: The first task's link with this submission's IDs filled in, for you to open once the submission is `in_progress`. Set when `task_completion` is "redirect" and `screening_outcome` is "passed"; null otherwise.
- `dashboard_url` (string, nullable) **(required)**: Who applied, who is in progress, who is awaiting review. Set exactly when `draft_editor` here is null. Do not read that as the opportunity being live: the `status` field reports several settled states (cancelled, expired, failed) as `draft`, so read status from `status` and treat this only as a link. Give this when the customer asks about progress or has submissions to review. Null if Terac cannot resolve the organization or project this link belongs to.
### 400: Invalid input data

- `message` (string) **(required)**: The error message
- `code` (string) **(required)**: The error code
- `issues` (array): An array of issues that were responsible for the error
  - `message` (string) **(required)**
### 401: The API key is missing, invalid, disabled, expired or revoked, its owner no longer exists, or it is not linked to an organization. The body is nested under `error`.

- `error` (object) **(required)**
  - `code` (string) **(required)**: one of: `UNAUTHORIZED`: The error code.
  - `message` (string) **(required)**: What went wrong.
### 403: Either the account that owns the API key is banned or deleted (from the key check, nested under `error`), or the key's owner lacks the organization permission this operation needs (from the operation, with `code` and `message` at the top level).

- One of: API key error (403)
  - `error` (object) **(required)**
    - `code` (string) **(required)**: one of: `FORBIDDEN`: The error code.
    - `message` (string) **(required)**: What went wrong.
- One of: Insufficient access error (403)
  - `message` (string) **(required)**: The error message
  - `code` (string) **(required)**: The error code
  - `issues` (array): An array of issues that were responsible for the error
    - `message` (string) **(required)**
### 404: Not found

- `message` (string) **(required)**: The error message
- `code` (string) **(required)**: The error code
- `issues` (array): An array of issues that were responsible for the error
  - `message` (string) **(required)**
### 429: The API key is rate limited. A key accepts 100 requests, then refuses every request until more than 60 seconds pass with no accepted request; refused requests do not extend the wait. Wait the number of seconds in `Retry-After`, then retry. The body is nested under `error`.

- `error` (object) **(required)**
  - `code` (string) **(required)**: one of: `RATE_LIMITED`: The error code.
  - `message` (string) **(required)**: What went wrong.
### 500: Internal server error

- `message` (string) **(required)**: The error message
- `code` (string) **(required)**: The error code
- `issues` (array): An array of issues that were responsible for the error
  - `message` (string) **(required)**