> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.awardspring.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.awardspring.com/_mcp/server.

# Create a reviewer.

POST https://api.awardspring.com/api/v1/reviewers
Content-Type: application/json

Adds one reviewer account, with the same rules as adding a reviewer from the Users list in the AwardSpring
admin interface: a first name, a last name and a well-formed email are required, each at most 50
characters. As in the admin interface, a new reviewer gets no welcome email.

An email that already has an account: by default the email must not already be used by any account
at the institution, ignoring upper and lower case; if it is, this returns `400 email_already_exists`.
With `if_email_in_use` set to `add_role`, this does what the admin interface does instead, and no
second account is made. When an active account at the institution has the email, the reviewer role is added
to that account, which keeps its name, email and password, and this returns `200` with that account as
the reviewer. An account that already has the reviewer role is returned unchanged, also with `200`. The
role is never added to an account that holds an administrator role, and this returns
`400 email_already_exists` instead, as it does for an email whose account is deactivated, merged, or one
the admin interface would not change. When only an archived account has the email, that account gets no
role: a new reviewer is made with `201` and the email moves to them, as in the admin interface.
The `AwardSpring-Existing-Account` header says which of these happened: `role_added`,
`role_already_held` or `email_moved_from_archived`. It is absent for an ordinary create, and from
a response replayed for an `Idempotency-Key`.

Idempotency: the request supports an optional `Idempotency-Key` header (any
printable-ASCII string, 1–255 chars — UUIDs work well). When supplied, the same key plus the same
request body within 24 hours returns the original response verbatim without creating a duplicate
reviewer. The same key with a different body returns `422 idempotency_key_request_mismatch`; a
concurrent retry while the first call is still executing returns `409 idempotency_key_in_flight`.

Reference: https://docs.awardspring.com/api-reference/reviewers/create

## Authentication

- `X-Spring-API-Key` header (required) — Secret API key issued per institution. Send it on every request.

## Servers

- `https://api.awardspring.com` (UnitedStates, default)
- `https://api.awardspring.ca` (Canada)

## Request

### Body (application/json)

This endpoint expects a CreateReviewerV1Request.

- `first_name` (string, required, nullable) — The reviewer's first name. Required, at most 50 characters. Example: `"Pat"`.
- `last_name` (string, required, nullable) — The reviewer's last name. Required, at most 50 characters. Example: `"Lee"`.
- `email` (string, required, nullable) — The reviewer's email, which is also how they sign in. Required, at most 50 characters, and not already used by any account at the institution, ignoring upper and lower case, unless `if_email_in_use` is `add_role`. Example: `"pat.lee@school.edu"`.
- `if_email_in_use` (enum, optional, nullable) — Optional. What to do when an account at the institution already has the email. `error`, the default, returns `400 email_already_exists`. `add_role` does what the Users list's Add user window does: the reviewer role is added to that account instead of making a second one. Any other value returns `400 validation_failed`.
  - Allowed values: `error`, `add_role`

## Response

### 200

OK

- `object` (string, optional, nullable) — Stripe-style resource discriminator. Stable, snake_case string naming the resource type. Serialized first so it reads as the leading field of every V1 resource body.
- `id` (string, optional, nullable) — The stored ID of the reviewer's user record. It is also their `id` in `GET /api/v1/people`.
- `first_name` (string, optional, nullable)
- `last_name` (string, optional, nullable)
- `email` (string, optional, nullable)
- `created_date` (integer, optional) — When the reviewer account was created, as UTC epoch seconds.

### 201

Created

- `object` (string, optional, nullable) — Stripe-style resource discriminator. Stable, snake_case string naming the resource type. Serialized first so it reads as the leading field of every V1 resource body.
- `id` (string, optional, nullable) — The stored ID of the reviewer's user record. It is also their `id` in `GET /api/v1/people`.
- `first_name` (string, optional, nullable)
- `last_name` (string, optional, nullable)
- `email` (string, optional, nullable)
- `created_date` (integer, optional) — When the reviewer account was created, as UTC epoch seconds.

## Errors

### 400 Bad Request Error

Bad Request

- `error` (V1ErrorBody, optional) — Stripe-shaped error object for the `/api/v1/*` surface. Serialized snake\_case (via V1SnakeCaseNamingStrategy) and always nested under the `error` key of a V1ErrorEnvelope: `{ "error": { "type": "...", "code": "...", "message": "...", "param": null, "doc_url": null, "recovery": "..." } }`

### 403 Forbidden Error

Forbidden

- `error` (V1ErrorBody, optional) — Stripe-shaped error object for the `/api/v1/*` surface. Serialized snake\_case (via V1SnakeCaseNamingStrategy) and always nested under the `error` key of a V1ErrorEnvelope: `{ "error": { "type": "...", "code": "...", "message": "...", "param": null, "doc_url": null, "recovery": "..." } }`

### 409 Conflict Error

Conflict

- `error` (V1ErrorBody, optional) — Stripe-shaped error object for the `/api/v1/*` surface. Serialized snake\_case (via V1SnakeCaseNamingStrategy) and always nested under the `error` key of a V1ErrorEnvelope: `{ "error": { "type": "...", "code": "...", "message": "...", "param": null, "doc_url": null, "recovery": "..." } }`

### 422 Unprocessable Entity Error

Unprocessable Content

- `error` (V1ErrorBody, optional) — Stripe-shaped error object for the `/api/v1/*` surface. Serialized snake\_case (via V1SnakeCaseNamingStrategy) and always nested under the `error` key of a V1ErrorEnvelope: `{ "error": { "type": "...", "code": "...", "message": "...", "param": null, "doc_url": null, "recovery": "..." } }`

### 429 Too Many Requests Error

Rate limit exceeded for this API key. Retry after the interval in the `Retry-After` header.

- `error` (V1ErrorBody, optional) — Stripe-shaped error object for the `/api/v1/*` surface. Serialized snake\_case (via V1SnakeCaseNamingStrategy) and always nested under the `error` key of a V1ErrorEnvelope: `{ "error": { "type": "...", "code": "...", "message": "...", "param": null, "doc_url": null, "recovery": "..." } }`

## Types

### V1ErrorBody

Stripe-shaped error object for the `/api/v1/*` surface. Serialized snake\_case (via V1SnakeCaseNamingStrategy) and always nested under the `error` key of a V1ErrorEnvelope: `{ "error": { "type": "...", "code": "...", "message": "...", "param": null, "doc_url": null, "recovery": "..." } }`

- `type` (string, optional, nullable) — Coarse error category from V1ErrorType (e.g. `invalid_request_error`).
- `code` (string, optional, nullable) — Stable fine-grained machine code from ErrorCode (e.g. `donor_not_found`).
- `message` (string, optional, nullable) — Human-readable summary safe to surface to the caller.
- `param` (string, optional, nullable) — The request parameter the error relates to, when attributable to a single field; otherwise null.
- `doc_url` (string, optional, nullable) — Link to documentation for this error, when one exists; otherwise null.
- `recovery` (string, optional, nullable) — Plain-language guidance on how to recover from this error, suitable for showing to the person who triggered it. Not present on every error.
- `details` (any, optional) — Optional structured payload (e.g. a list of ValidationDetail for validation failures). Omitted from the wire when null so the canonical Stripe shape stays minimal.

## Examples

### Example 1

**Request**

```json
{
  "first_name": "string",
  "last_name": "string",
  "email": "string"
}
```

**Response**

```json
{
  "object": "string",
  "id": "string",
  "first_name": "string",
  "last_name": "string",
  "email": "string",
  "created_date": 1
}
```

**SDK Code**

```python
import requests

url = "https://api.awardspring.com/api/v1/reviewers"

payload = {
    "first_name": "string",
    "last_name": "string",
    "email": "string"
}
headers = {
    "X-Spring-API-Key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.awardspring.com/api/v1/reviewers';
const options = {
  method: 'POST',
  headers: {'X-Spring-API-Key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"first_name":"string","last_name":"string","email":"string"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.awardspring.com/api/v1/reviewers"

	payload := strings.NewReader("{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-Spring-API-Key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.awardspring.com/api/v1/reviewers")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-Spring-API-Key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.awardspring.com/api/v1/reviewers")
  .header("X-Spring-API-Key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.awardspring.com/api/v1/reviewers', [
  'body' => '{
  "first_name": "string",
  "last_name": "string",
  "email": "string"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-Spring-API-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.awardspring.com/api/v1/reviewers");
var request = new RestRequest(Method.POST);
request.AddHeader("X-Spring-API-Key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "X-Spring-API-Key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "first_name": "string",
  "last_name": "string",
  "email": "string"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.awardspring.com/api/v1/reviewers")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### Example 2

**Request**

```json
{
  "first_name": "string",
  "last_name": "string",
  "email": "string"
}
```

**Response**

```json
{
  "object": "string",
  "id": "string",
  "first_name": "string",
  "last_name": "string",
  "email": "string",
  "created_date": 1
}
```

**SDK Code**

```python
import requests

url = "https://api.awardspring.com/api/v1/reviewers"

payload = {
    "first_name": "string",
    "last_name": "string",
    "email": "string"
}
headers = {
    "X-Spring-API-Key": "<apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.awardspring.com/api/v1/reviewers';
const options = {
  method: 'POST',
  headers: {'X-Spring-API-Key': '<apiKey>', 'Content-Type': 'application/json'},
  body: '{"first_name":"string","last_name":"string","email":"string"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.awardspring.com/api/v1/reviewers"

	payload := strings.NewReader("{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("X-Spring-API-Key", "<apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.awardspring.com/api/v1/reviewers")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-Spring-API-Key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.awardspring.com/api/v1/reviewers")
  .header("X-Spring-API-Key", "<apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.awardspring.com/api/v1/reviewers', [
  'body' => '{
  "first_name": "string",
  "last_name": "string",
  "email": "string"
}',
  'headers' => [
    'Content-Type' => 'application/json',
    'X-Spring-API-Key' => '<apiKey>',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.awardspring.com/api/v1/reviewers");
var request = new RestRequest(Method.POST);
request.AddHeader("X-Spring-API-Key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"first_name\": \"string\",\n  \"last_name\": \"string\",\n  \"email\": \"string\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "X-Spring-API-Key": "<apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "first_name": "string",
  "last_name": "string",
  "email": "string"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.awardspring.com/api/v1/reviewers")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```