Managing API Keys¶
API keys provide machine-to-machine access to the IPTO API. This guide covers creating keys with specific scopes, controlling dataset access, inspecting and revoking keys, and security best practices for production.
Creating keys with specific scopes¶
Every API key is scoped to a set of permissions. Assign only the scopes your integration needs.
Available scopes¶
| Scope | Description |
|---|---|
datasets:read | List and inspect datasets. |
datasets:write | Create and update datasets. |
objects:write | Upload and manage objects within datasets. |
search:query | Execute search queries against the marketplace. |
usage:read | View usage and activity reports. |
keys:write | Create and manage API keys (for automation). |
billing:read | View billing, invoices, and spend summaries. |
admin:* | Full administrative access (use with extreme caution). |
Example: search-only key¶
import requests
BASE = "https://api.ipto.ai"
headers = {"Authorization": f"Bearer {token}"}
resp = requests.post(
f"{BASE}/v1/api-keys",
headers=headers,
json={
"name": "search-agent-prod",
"scopes": ["search:query", "datasets:read"],
},
)
resp.raise_for_status()
key = resp.json()["data"]
print(f"Key ID: {key['api_key_id']}")
print(f"Secret: {key['secret']}")
const BASE = "https://api.ipto.ai";
const headers = {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
};
const res = await fetch(`${BASE}/v1/api-keys`, {
method: "POST",
headers,
body: JSON.stringify({
name: "search-agent-prod",
scopes: ["search:query", "datasets:read"],
}),
});
const key = (await res.json()).data;
console.log(`Key ID: ${key.api_key_id}`);
console.log(`Secret: ${key.secret}`);
Example: upload-only key¶
Save your secret immediately
The API key secret is returned only once at creation time. Store it in a secrets manager or environment variable. If you lose it, revoke the key and create a new one.
Choosing dataset access mode¶
Every API key has a dataset_access_mode that controls which datasets it can interact with.
| Mode | Description |
|---|---|
all_available | The key can access all datasets currently available to the tenant. This is the default. |
allow_list | The key can only access the datasets explicitly listed in dataset_ids. |
Creating a key with all_available (default)¶
When you omit dataset_access_mode, the key defaults to all_available:
curl -X POST https://api.ipto.ai/v1/api-keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "general-search-key",
"scopes": ["search:query", "datasets:read"]
}'
Creating a key with allow_list¶
Restrict the key to specific datasets by setting dataset_access_mode to allow_list and providing the dataset IDs:
Key restrictions can only narrow access
An API key's allow_list can only restrict access to a subset of datasets the tenant already has access to. It cannot grant access to datasets the tenant does not have permission to use.
Granting and revoking dataset access¶
Use PATCH /v1/api-keys/{api_key_id} to update the dataset access list on an existing key.
Adding datasets to an allow list¶
const patchRes = await fetch(`${BASE}/v1/api-keys/${apiKeyId}`, {
method: "PATCH",
headers,
body: JSON.stringify({
dataset_access_mode: "allow_list",
dataset_ids: ["dset_legal_001", "dset_legal_002", "dset_legal_003"],
}),
});
const updated = (await patchRes.json()).data;
console.log(`Datasets: ${updated.dataset_ids}`);
Removing dataset access¶
To remove a dataset from the allow list, send the updated list without that dataset:
curl -X PATCH https://api.ipto.ai/v1/api-keys/$API_KEY_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dataset_ids": ["dset_legal_001", "dset_legal_003"]
}'
Switching back to all_available¶
curl -X PATCH https://api.ipto.ai/v1/api-keys/$API_KEY_ID \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dataset_access_mode": "all_available",
"dataset_ids": []
}'
Listing and inspecting keys¶
List all keys for your tenant¶
const listRes = await fetch(`${BASE}/v1/api-keys`, {
headers: { Authorization: `Bearer ${token}` },
});
const keys = (await listRes.json()).data;
for (const k of keys) {
console.log(
`${k.api_key_id} ${k.name} scopes=${k.scopes} ` +
`mode=${k.dataset_access_mode} last_used=${k.last_used_at ?? "never"}`
);
}
Response:
{
"data": [
{
"api_key_id": "key_abc123",
"name": "search-agent-prod",
"scopes": ["search:query", "datasets:read"],
"dataset_access_mode": "all_available",
"dataset_ids": [],
"created_at": "2026-04-01T10:00:00Z",
"last_used_at": "2026-04-05T08:30:00Z"
},
{
"api_key_id": "key_def456",
"name": "legal-team-search",
"scopes": ["search:query", "datasets:read"],
"dataset_access_mode": "allow_list",
"dataset_ids": ["dset_legal_001", "dset_legal_002"],
"created_at": "2026-04-03T14:00:00Z",
"last_used_at": null
}
],
"request_id": "req_020",
"timestamp": "2026-04-05T12:00:00Z"
}
Note
The secret field is never returned in list or inspect responses. It is only available at creation time.
Revoking keys¶
Revoke a key immediately when it is compromised, no longer needed, or being rotated out.
Revocation is immediate. Any in-flight requests using the revoked key will fail with 401 unauthorized.
Security best practices¶
Follow these guidelines to keep your IPTO integration secure.
Rotate keys regularly¶
Set a rotation schedule -- quarterly at minimum, monthly for high-sensitivity integrations. The rotation workflow is:
- Create a new key with the same scopes and dataset access.
- Update your application to use the new key.
- Verify the new key is working.
- Revoke the old key.
Use least-privilege scopes¶
Assign only the scopes each integration actually needs:
| Integration | Recommended scopes |
|---|---|
| Search agent | search:query, datasets:read |
| Upload pipeline | datasets:write, objects:write |
| Billing dashboard | billing:read, usage:read |
| Full automation | datasets:read, datasets:write, objects:write, search:query |
Avoid granting admin:* or keys:write to automated systems unless absolutely necessary.
Use allow_list for production¶
In production, prefer dataset_access_mode: "allow_list" over all_available:
all_availableautomatically grants access to any new dataset added to your tenant. This is convenient for development but risky in production -- a newly added dataset might expose data your agent should not access.allow_listgives you explicit control. You decide which datasets each key can reach.
Store secrets securely¶
- Never commit API key secrets to source control.
- Use a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) or encrypted environment variables.
- Never log API key secrets in application logs.
Monitor key usage¶
Review last_used_at on your keys regularly. Keys that have not been used in months are candidates for revocation.
# List keys and check for stale ones
curl -s https://api.ipto.ai/v1/api-keys \
-H "Authorization: Bearer $TOKEN" | jq '.data[] | {name, last_used_at}'
Audit key lifecycle events¶
Every key creation and revocation emits an audit event. Review your audit log periodically to ensure no unauthorized keys have been created.
Next steps¶
- Searching Data -- Use your scoped API key to run search queries.
- Uploading Data -- Create keys for your upload automation pipelines.
- Provider Analytics -- Monitor dataset performance and revenue.