# Idealist Apply API documentation
## Target Audience
This documentation is aimed at software developers wishing to allows users to apply to an
[Idealist.org](https://www.idealist.org) listing (e.g. express interest in a volunteer opportunity)
from directly within your 3rd party platform without needing to visit an external link.
## General Overview
Idealist.org provides the [Listings API](/listings-api) to consume listings posted on Idealist.org.
You can then use the Apply API to create listing applications for volunteer listings.
The API is a JSON-based REST API. You'll need an API key.
## Support
You must contact the Idealist support staff to obtain an API key: .
For technical questions regarding the API during the implementation phase, contact .
For support afterward, contact .
## Before Starting
Start by implementing the [Listings API](/listings-api) to fetch the volunteer listings
using `GET /api/v1/listings/volops` and `GET /api/v1/listings/volops/{volop_id}`.
## Technical Overview
Creating a listing application consists mostly of making a single `POST` request to `https://www.idealist.org/api/v1/listings/volops/{volop_id}/apply`.
For example:
```python
import requests
headers = {"Accept": "application/json"}
url = f"https://www.idealist.org/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [],
"requestedAttachmentsHash": hash,
}
requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
```
When the application is created, the success HTTP status code is `201 Created`.
## Basic Fields
The fields in the request body are all required.
`email`: strings.
`firstName`: string.
`lastName`: string.
`attachments`: array of objects. [Details](#attachments).
`requestedAttachmentsHash`: string. [Details](#requestedAttachmentsHash).
## Attachments
### Attachments Overview
The `attachments` are responses to `requestedAttachments` – the questions that were requested by the person who posted the listing.
For example, in the response of the request `GET /api/v1/listings/volops/{volop_id}`, you see `requestedAttachments` and `requestedAttachmentsHash`:
```json
...
"requestedAttachments": [
{
"type": "TEXT",
"label": "What's your favorite color?"
"isRequired": true
},
{
"type": "URL",
"label": "What's your website?"
"isRequired": false
}
],
"requestedAttachmentsHash": "3a608fbbf000e3bbcd56a4995b6060caad062ccecb5a4df848e0260534100835",
...
```
You could then create the application with a request body that would look like this:
```json
{
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [
{
"type": "TEXT",
"text": "Blue"
},
{
"type": "URL",
"url": "https://www.example.com/john.doe"
}
],
"requestedAttachmentsHash": "3a608fbbf000e3bbcd56a4995b6060caad062ccecb5a4df848e0260534100835",
}
```
In `requestedAttachments`:
`label` is the human readable string that should be exposed to the user.
`type` specifies which type of attachment is expected.
`isRequired` specifies if the attachment is required or not.
### Attachments Required or Not
An requested attachment can be optional. Not required.
In this example, the second requested attachment (the question `"What's your website?"`) is not required.
```json
...
"requestedAttachments": [
{
"type": "TEXT",
"label": "What's your favorite color?"
"isRequired": true
},
{
"type": "URL",
"label": "What's your website?"
"isRequired": false
}
],
"requestedAttachmentsHash": "3a608fbbf000e3bbcd56a4995b6060caad062ccecb5a4df848e0260534100835",
...
```
Meaning that we could create a listing application without a response to that question.
```json
{
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [
{
"type": "TEXT",
"text": "Blue"
},
{
"type": "URL",
"url": null
}
],
"requestedAttachmentsHash": "3a608fbbf000e3bbcd56a4995b6060caad062ccecb5a4df848e0260534100835",
}
```
However, if an attachment is required (`isRequired` is `true`), you will receive a validation error when
trying to create a listing application without that attachment. For example, sending this request body:
```json
{
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [
{
"type": "TEXT",
"text": null
},
{
"type": "URL",
"url": "https://www.example.com/john.doe"
}
],
"requestedAttachmentsHash": "3a608fbbf000e3bbcd56a4995b6060caad062ccecb5a4df848e0260534100835",
}
```
would result in a `400 Bad Request` response, with a response body that would look like this:
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "attachments.0",
"description": "Required"
}
]
}
```
### Attachments Types
#### Attachments Type `TEXT`
```json
{
"type": "TEXT",
"label": "What's your favorite color?"
"isRequired": true
}
```
This type of attachment would usually be shown as a ``.
The value will go in the `text` attribute. It can contain new line characters (`"\n"`).
Here's an example response:
```json
{
"type": "TEXT",
"text": "I like blue.\nI also like green, an yellow."
}
```
#### Attachments Type `URL`
```json
{
"type": "URL",
"label": "What's your website?"
"isRequired": true
}
```
This type of attachment would usually be shown as an `` or ``.
The value will go in the `url` attribute. It can't contain new line characters (`"\n"`).
Here's an example response:
```json
{
"type": "URL",
"url": "https://www.example.com/john.doe"
}
```
#### Attachments Type `PHONE`
```json
{
"type": "PHONE",
"label": "What's your phone number?"
"isRequired": true
}
```
This type of attachment would usually be shown as an ``.
The value will go in the `phone` attribute. It must be a valid international phone number and start with "+".
Here's an example response:
```json
{
"type": "PHONE",
"phone": "+19175551234"
}
```
#### Attachments Type `OPTION_SINGLE`
```json
{
"type": "OPTION_SINGLE",
"label": "Pick one of those choices"
"isRequired": true
"options": ["Option One", "Optione Two", "Option Three"]
}
```
This type of attachment would usually be shown as ``.
The value will go in the `option` attribute. Only a single of the options can be sent.
Here's an example response:
```json
{
"type": "OPTION_SINGLE",
"option": "Option Two"
}
```
#### Attachments Type `OPTION_MANY`
```json
{
"type": "OPTION_MANY",
"label": "Pick one of those choices"
"isRequired": true
"options": ["Option One", "Optione Two", "Option Three"]
}
```
This type of attachment would usually be shown as ``.
The value will go in the `options` attribute. Many of the options can be sent.
Here's an example response:
```json
{
"type": "OPTION_MANY",
"options": ["Option Two", "Option Three"]
}
```
When the field is required, the `options` array must contain at least one value.
#### Attachments Type `FILE` (not required)
```json
{
"type": "FILE",
"label": "Resume"
"isRequired": true
"mimeTypes": [
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/msword",
"image/jpeg",
"image/png",
"text/plain"
]
}
```
Files require more implementation steps than other attachment types. Due to the complexity of implementation,
Idealist does not require file transfer via API as part of the integration. It is available for any partner who has the
capacity to send this data since some volunteer opportunities request files such as a resume.
Files have to be uploaded separately before sending the API request to create the listing application.
The process looks like this:
1. Tell Idealist that you'll upload a file. You'll obtain a file ID, a URL and form parameters to use.
2. Upload the file using the URL and parameters from 1.
3. Tell Idealist that you completed the file upload.
4. Wait until the file has been processed.
5. Create the listing application.
If the user does not upload a file, or if you choose to not implement file upload, simply submit `null` for the `fileId`.
```json
{
"type": "FILE",
"fileId": null
}
```
##### Attachments Type `FILE` step 1 — Obtain upload details
`POST /api/v1/files`
```json
{
"filename": "filename.pdf",
"size": 14646,
"mimeType": "application/pdf"
}
```
`size` is the number of bytes of the file.
```json
{
"file": {
"id": "44d46d942eea4684896f5caa1ee2863b",
"created": "2025-04-20T14:46:14.347009Z",
"updated": "2025-04-20T14:46:14.347012Z",
"filename": "filename.pdf",
"mimetype": "application/pdf",
"size": 14646,
"status": "DECLARED",
"url": null
},
"params": {
"url": "https://example.com/url-to-upload",
"fields": {
"key": "44d46d942eea4684896f5caa1ee2863b/filename.pdf",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "AZIAXS2X3N3ONIKXL2UC/20200217/us-east-2/s3/aws4_request",
"x-amz-date": "20200217T204548Z",
"policy": "eyJleHBpcmF0aW9uIjogIjIwgjAtMDItMTdUMjA6NTU6NDhaIiwgImNvbmTpdGlvbnMiOiBbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDEsIDIwOTcxNTIwMF0sIHqiYnVja2V0IjogImRldi1hbbRvnW5lLWluYm94In0sIHsia2V5IjogImFiYy9hc2QifSwgeyJ4LWFtei1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQVhTMlgzTjNPTklQWEwyVUMvMjAyMDAyMTcvdXMtZWFzdC0yL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAyMDAyMTdUMjA0NTQ4WiJ9XX0=",
"x-amz-signature": "cbbe28e8a9ecf26f81b794d5bfa8b13b39622beedbd85b66450671c0b7k6df95"
}
}
}
```
##### Attachments Type `FILE` step 2 — Upload the file
Using the `params` part of the response from step 1, send to the `url`, all the `fields` in a `POST` request
that's using the `multipart/form-data` encoding type.
For example, either using code:
```python
url = "https://example.com/url-to-upload"
params = {
"key": "44d46d942eea4684896f5caa1ee2863b/filename.pdf",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "AZIAXS2X3N3ONIKXL2UC/20200217/us-east-2/s3/aws4_request",
"x-amz-date": "20200217T204548Z",
"policy": "eyJleHBpcmF0aW9uIjogIjIwgjAtMDItMTdUMjA6NTU6NDhaIiwgImNvbmTpdGlvbnMiOiBbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDEsIDIwOTcxNTIwMF0sIHqiYnVja2V0IjogImRldi1hbbRvnW5lLWluYm94In0sIHsia2V5IjogImFiYy9hc2QifSwgeyJ4LWFtei1hbGdvcml0aG0iOiAiQVdTNC1ITUFDLVNIQTI1NiJ9LCB7IngtYW16LWNyZWRlbnRpYWwiOiAiQUtJQVhTMlgzTjNPTklQWEwyVUMvMjAyMDAyMTcvdXMtZWFzdC0yL3MzL2F3czRfcmVxdWVzdCJ9LCB7IngtYW16LWRhdGUiOiAiMjAyMDAyMTdUMjA0NTQ4WiJ9XX0=",
"x-amz-signature": "cbbe28e8a9ecf26f81b794d5bfa8b13b39622beedbd85b66450671c0b7k6df95"
}
files = {
"file": (
'filename.pdf',
open("/path/to/file.pdf", "rb"),
'application/pdf'
)
}
requests.post(url, data=params, files=files)
```
or using an HTML form:
```html
```
Notice that the API key is not used when uploading the file. The form parameters include a signature for this specific file.
##### Attachments Type `FILE` step 3 — Mark upload completed
Once the file has been uploaded, tell Idealist about it by sendnig a `POST` request to `/api/v1/files/{file_id}/done`, with an empty request body.
```python
url = f"https://www.idealist.org/api/v1/files/{file_id}/done"
requests.post(url, headers={"Accept": "application/json"}, auth=(API_KEY, ""))
```
The response status code should be `202 Accepted`, meaning that Idealist will not process the uploaded file.
##### Attachments Type `FILE` step 4 — Wait upload processed
After step 3, the status of the file should go from `"DECLARED"` to `"PROCESSING"` then `"UPLOADED"`.
You should wait for it to be `"UPLOADED"` before trying to create the listing application.
You can obtain the the status by making a `GET` request to `/api/v1/files/{file_id}`.
It should return a response that looks like this (status code `200 OK`):
```json
{
"file": {
"id": "44d46d942eea4684896f5caa1ee2863b",
"created": "2025-04-20T14:46:14.347009Z",
"updated": "2025-04-20T14:46:14.347012Z",
"filename": "filename.pdf",
"mimetype": "application/pdf",
"size": 14646,
"status": "UPLOADED",
"url": "https://example.com/url-to-download-file"
}
}
```
Notice the `status` that changed to `"UPLOADED"` and the `url` that changed from `null` to a string.
The `url` is a signed URL valid for 5 minutes.
Here's how it could look like, waiting for the file to have the `status` `"UPLOADED"`:
```python
from datetime import datetime, timezone, timdela
timeout = datetime.now(timezone.utc) + timeldeta(seconds=120)
processed = False
while not processed and datetime.now(timezone.utc) < timeout:
response = requests.get(f"/api/v1/files/{file_id}", headers={"Accept": "application/json"}, auth=(API_KEY, ""))
if response.status_code == 200 and resopnse.json()["file"]["status"] == "UPLOADED":
procesed = True
```
A `status` of `"ERRORED"` means that some technical error may have occured and the process should be started over from step 1.
##### Attachments Type `FILE` step 5 — Create the listing application
You can now use the file in the `attachments` when creating the listing application.
```json
{
"type": "FILE",
"fileId": "44d46d942eea4684896f5caa1ee2863b"
}
```
##### Attachments Type `FILE` mimetype
In the `requestedAttachments`, notice that there's an attribute called `mimeTypes`:
```json
{
"type": "FILE",
"label": "Resume"
"isRequired": true
"mimeTypes": [
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/msword",
"image/jpeg",
"image/png",
"text/plain"
]
}
```
It lists the acceptable mimetypes that the uploaded file can have. Currently, it's either
```
[
"application/pdf",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/msword",
"image/jpeg",
"image/png",
"text/plain"
]
```
or
```
[
"application/pdf",
]
```
but could change in the future.
## requestedAttachmentsHash
In the request body of `POST /api/v1/listings/volops/{volop_id}/apply`, there's one attribute that's named
`requestedAttachmentsHash`. It's a string that looks like `"3a608fbbf000e3bbcd56a4995b6060caad062ccecb5a4df848e0260534100835"`
that was obtained when fetching the listing from `GET /api/v1/listings/volops/{volop_id}`.
You simply pass the same string back to the API. The goal is to ensure that the requested attachments
didn't change since fetching them from `GET /api/v1/listings/volops/{volop_id}`.
If it had changed, causing a mismatch, the response will be a `400 Bad Request` that looks like this:
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "requestedAttachmentsHash",
"description": "The form to apply has been updated since you opened the page. Please reload the page and submit the form again."
}
]
}
```
## Validation
The API will respond with a response body of `400 Bad Request` when there are validation errors.
For example:
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "requestedAttachmentsHash",
"description": "The form to apply has been updated since you opened the page. Please reload the page and submit the form again."
}
]
}
```
The `location` attribute specifies where the request the validation error occured. At the moment, only `"body"` can have validation errors.
The `name` attribute will specify which field of the request has the validation error.
The `description` attribute contains a human readable string that can be displayed next to the field.
### Validation example — required email
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "email",
"description": "Required"
}
]
}
```
### Validation example — missing required attachment
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "attachments.0",
"description": "Required"
}
]
}
```
Note the index of the attachment (`0`).
### Validation example — invalid phone number
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "attachments.0.phone",
"description": "Invalid phone number"
}
]
}
```
### Validation example — file upload not complete
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "attachments.0.fileId",
"description": "File upload not complete"
}
]
}
```
Note the index of the attachment (`0`), and also that the name can be deeper than
the index (`fileId` in `attachments.0.fileId`) but should be displayed for the attachment
with index `0` in the form anyway.
### Validation example — multiple errors
```json
{
"status": "error",
"errors": [
{
"location": "body",
"name": "email",
"description": "Required"
},
{
"location": "body",
"name": "attachments.0",
"description": "Required"
},
{
"location": "body",
"name": "attachments.1",
"description": "Required"
}
]
}
```
There can be many validation errors.
## Sandbox
You can use the sandbox to help during the implementation phase.
The base URL is https://api-sandbox.idealist.org instead of https://www.idealist.org.
The API key is `ecc53cf923890fa0830c5972daa8140a` and it let's you use the Apply API and also fetch individual volunteer listings.
There are a few volunteer listings included so that you can test spefific cases:
* `60c6dcbf5e32475ca8d43f0001370a2b`: A volunteer listing with no `requestedAttachments`. A good place to start when trying to make a request to create a listing application.
* `3646ff78d1d94d13af0425b126572083`: A volunteer listing with a required `TEXT` `requestedAttachment`.
* `7fc954b7be2946ecbaed26ac8583de7d`: A volunteer listing with a optional `TEXT` `requestedAttachment`.
* `8287328dfa0e4f3a982fbf2208c1a0cc`: A volunteer listing with a required `URL` `requestedAttachment`.
* `e9e40dd5810c47dba0eb65e7698c00f8`: A volunteer listing with a optional `URL` `requestedAttachment`.
* `cf8f110cfd664bc3be24dbd9f1132d03`: A volunteer listing with a required `PHONE` `requestedAttachment`.
* `cb1ac7f51d834ba3a4f00d1031ab4622`: A volunteer listing with a optional `PHONE` `requestedAttachment`.
* `26c6d8afe9a34c15a0d990db5ec8f269`: A volunteer listing with a required `OPTION_SINGLE` `requestedAttachment`.
* `6435120fa7ca4c69abaf330b1b66274d`: A volunteer listing with a optional `OPTION_SINGLE` `requestedAttachment`.
* `970244717bcc493981d76d2b3dc14d93`: A volunteer listing with a required `OPTION_MANY` `requestedAttachment`.
* `1d8dc581d14d4894b9a766a0bda5330e`: A volunteer listing with a optional `OPTION_MANY` `requestedAttachment`.
* `9defd5667aff4d10a88e06663f45e95a`: A volunteer listing with a many `requestedAttachment`.
### Sandbox — Example: no requested attachment
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "60c6dcbf5e32475ca8d43f0001370a2b"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `TEXT` attachment
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "3646ff78d1d94d13af0425b126572083"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "TEXT",
"text": "This is the answer",
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `TEXT` attachment (optional)
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "7fc954b7be2946ecbaed26ac8583de7d"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "TEXT",
"text": None,
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `URL` attachment
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "8287328dfa0e4f3a982fbf2208c1a0cc"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "URL",
"url": "https://www.example.com",
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `URL` attachment (optional)
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "e9e40dd5810c47dba0eb65e7698c00f8"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "URL",
"url": None,
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `PHONE` attachment
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "cf8f110cfd664bc3be24dbd9f1132d03"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "PHONE",
"phone": "+19175551234",
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `PHONE` attachment (optional)
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "cb1ac7f51d834ba3a4f00d1031ab4622"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "PHONE",
"phone": None,
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `OPTION_SINGLE` attachment
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "26c6d8afe9a34c15a0d990db5ec8f269"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "OPTION_SINGLE",
"option": "Blue",
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `OPTION_SINGLE` attachment (optional)
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "6435120fa7ca4c69abaf330b1b66274d"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "OPTION_SINGLE",
"option": None,
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `OPTION_MANY` attachment
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "970244717bcc493981d76d2b3dc14d93"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "OPTION_MANY",
"options": ["Blue", "Red"],
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: `OPTION_MANY` attachment (optional)
```python
import requests
API_KEY = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "1d8dc581d14d4894b9a766a0bda5330e"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "OPTION_MANY",
"options": [],
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_KEY, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
### Sandbox — Example: many requested attachments
```python
import requests
API_TOKEN = "ecc53cf923890fa0830c5972daa8140a"
BASE_URL = "https://api-sandbox.idealist.org"
volop_id = "9defd5667aff4d10a88e06663f45e95a"
# fetch listing
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}"
response = requests.get(url, auth=(API_TOKEN, ""), headers=headers, timeout=35)
assert response.status_code == 200
hash = response.json()["volop"]["requestedAttachmentsHash"]
# create listing application
headers = {"Accept": "application/json"}
url = f"{BASE_URL}/api/v1/listings/volops/{volop_id}/apply"
request_body = {
"email": "john.doe@example.com",
"firstName": "John",
"lastName": "Doe",
"attachments": [{
"type": "TEXT",
"text": "some text",
}, {
"type": "TEXT",
"text": None,
}, {
"type": "URL",
"url": "https://www.example.com",
}, {
"type": "URL",
"url": None,
}, {
"type": "PHONE",
"phone": "+12343456789",
}, {
"type": "PHONE",
"phone": None,
}, {
"type": "OPTION_SINGLE",
"option": "Red",
}, {
"type": "OPTION_SINGLE",
"options": None,
}, {
"type": "OPTION_MANY",
"options": ["Red", "Yellow"],
}, {
"type": "OPTION_MANY",
"options": [],
}, {
"type": "FILE",
"fileId": None,
}, {
"type": "FILE",
"fileId": None,
}],
"requestedAttachmentsHash": hash,
}
response = requests.post(url, json=request_body, auth=(API_TOKEN, ""), headers=headers, timeout=35)
assert response.status_code == 201
```
## API Reference - Status Codes
The API will return the following HTTP status codes:
- 200 OK
- 201 Created
- 202 Accepted
- 302 Found - if you are being redirected to /login, make sure that you are passing `Accept: application/json`
- 400 Bad Request - the request contained invalid data. Have a look at the response body for details.
- 401 Unauthorized - check your API key
- 404 Not Found - It can mean a listing has expired, has been hidden, etc. Otherwise, make sure the URL is correct.
## Changelog
- 2025-07-13 Add example with many requested attachments.
- 2025-06-02 Add phone number format validation.
- 2025-04-30 Initial version.