Read your surveys, responses, and NPS. Send surveys to your contacts.
The RallyNPS API v1 lets you read your surveys, responses, and NPS, and
send a survey to a list of contacts. It is a plain JSON API over HTTPS.
The base URL is https://nps.rallytasks.com. Every path below
is relative to that base.
A key belongs to one organization. Every response is scoped to that organization, so you only ever see your own data.
Every request must include your API key in the X-API-Key
header. Create a key inside the app: sign in at /app, open
Settings → API Keys, then choose
+ New API Key. A key looks like
rnps_live_… and is shown only once at creation, so
store yours somewhere safe. If you lose it, revoke it and create a new one.
curl https://nps.rallytasks.com/api/v1/surveys \
-H "X-API-Key: rnps_live_your_key_here"
Keys carry permissions. A read key (the default) can call every
GET endpoint. The two writing endpoints
(POST /api/v1/surveys/:id/send and
POST /api/v1/contacts) need a key with the
write permission; without it they return 403.
Each API key may make up to 100 requests per minute.
Going over returns 429 with the body
{ "error": "Rate limit exceeded. Max 100 requests per minute." }.
Back off and retry after the window resets.
Every error uses the same envelope: a JSON object with a single
error string.
{
"error": "Survey not found"
}
| Status | Meaning |
|---|---|
400 | Bad request. A required field is missing or invalid. |
401 | Unauthorized. The X-API-Key header is missing, unknown, revoked, or expired. |
403 | Forbidden. The key lacks the write permission a writing endpoint needs, or a send would exceed your monthly send limit. |
404 | Not found. The path, survey, response, or contact does not exist in your organization. |
429 | Too many requests. The per-key rate limit was exceeded. |
List endpoints accept page (default 1) and
limit (default 50, max 200) query
parameters, and return a meta object with page,
limit, and total.
List your surveys, newest first.
| Query | Type | Description |
|---|---|---|
page | integer | Page number (default 1) |
limit | integer | Rows per page (default 50, max 200) |
curl https://nps.rallytasks.com/api/v1/surveys?limit=50 \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": [
{
"id": "a1b2c3d4-...",
"name": "Q3 relationship survey",
"question_text": "How likely are you to recommend us?",
"from_name": "Acme",
"from_email": "[email protected]",
"status": "active",
"sent_count": 240,
"response_count": 88,
"created_at": "2026-06-01T12:00:00.000Z",
"updated_at": "2026-06-20T09:15:00.000Z"
}
],
"meta": { "page": 1, "limit": 50, "total": 12 }
}
Get one survey with its rolled-up score stats. Returns 404 if the survey is not in your organization.
curl https://nps.rallytasks.com/api/v1/surveys/a1b2c3d4-... \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": {
"id": "a1b2c3d4-...",
"name": "Q3 relationship survey",
"question_text": "How likely are you to recommend us?",
"from_name": "Acme",
"from_email": "[email protected]",
"reply_to": "[email protected]",
"status": "active",
"sent_count": 240,
"response_count": 88,
"created_at": "2026-06-01T12:00:00.000Z",
"updated_at": "2026-06-20T09:15:00.000Z",
"stats": {
"total_responses": 88,
"avg_score": 8.4,
"promoters": 52,
"passives": 24,
"detractors": 12,
"nps": 45
}
}
}
nps and avg_score come back null, never a false 0.Send a survey to a list of contacts. Needs a key with the write permission.
status. There
is no polling step: every status is terminal when the response returns.
A contact already sent this survey comes back as duplicate
and is not sent again.
| Body field | Type | Description |
|---|---|---|
contacts | array | Required. Each item needs an email; name and properties are optional. |
curl -X POST https://nps.rallytasks.com/api/v1/surveys/a1b2c3d4-.../send \
-H "X-API-Key: rnps_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"contacts":[{"email":"[email protected]","name":"Jordan Lee"}]}'
{
"data": {
"survey_id": "a1b2c3d4-...",
"contacts_queued": 3,
"contacts": [
{ "id": "c1...", "email": "[email protected]", "token": "..." },
{ "id": "c2...", "email": "[email protected]", "token": "..." },
{ "id": "c3...", "email": "[email protected]", "token": "..." }
],
"results": [
{ "email": "[email protected]", "status": "sent" },
{ "email": "[email protected]", "status": "duplicate", "reason": "Already sent this survey" },
{ "email": "[email protected]", "status": "failed", "reason": "Mail provider rejected the message" },
{ "email": "not-an-email", "status": "invalid", "reason": "Invalid or missing email address" }
],
"sent": 1,
"duplicates": 1,
"invalid": 1,
"failed": 1
}
}
results holds one entry per submitted contact, each with a
terminal status and a reason when the email
did not go out. contacts holds one row per contact that had
a valid email, each with its id, email, and
survey token.
| status | Meaning |
|---|---|
"sent" | The mail provider accepted the message. |
"duplicate" | Skipped. Duplicate protection or a frequency rule suppressed this send. A reason is attached. |
"invalid" | Skipped. The email was missing or malformed. |
"failed" | The mail provider or handler errored. A reason is attached. |
The response also carries the counts sent,
duplicates, invalid, and failed.
contacts_queued is the number of contact rows touched
(created or reused) for this send. It is kept for backward
compatibility and is not a count of pending work.
Errors: 400 if contacts is missing or empty;
403 without the write permission;
404 if the survey is not in your organization. If the send
would push you past your organization's monthly send limit, the whole
request is rejected with 403 and this body (no contact is
sent):
{
"error": "Monthly send limit exceeded",
"code": "SEND_LIMIT_EXCEEDED",
"current_limit": 500,
"used": 480,
"requested": 50
}
List responses across your surveys, newest first.
| Query | Type | Description |
|---|---|---|
survey_id | UUID | Limit to one survey |
category | string | promoter, passive, or detractor |
date_from | ISO 8601 | On or after this time |
date_to | ISO 8601 | On or before this time |
search | string | Match on comment, contact email, or contact name |
page, limit | integer | See Pagination |
curl "https://nps.rallytasks.com/api/v1/responses?category=detractor" \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": [
{
"id": "e5f6...",
"survey_id": "a1b2c3d4-...",
"score": 9,
"comment": "Support fixed my issue fast.",
"ai_sentiment": "positive",
"ai_themes": ["support", "speed"],
"ai_churn_risk": "low",
"ai_summary": "Happy with fast support.",
"created_at": "2026-06-18T10:00:00.000Z",
"contact_email": "[email protected]",
"contact_name": "Jordan Lee",
"survey_name": "Q3 relationship survey"
}
],
"meta": { "page": 1, "limit": 50, "total": 88 }
}
ai_sentiment, ai_themes, ai_churn_risk, ai_summary) are filled only when AI is enabled for your organization.Get one response by ID. Returns 404 if it is not in your organization.
curl https://nps.rallytasks.com/api/v1/responses/e5f6... \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": {
"id": "e5f6...",
"survey_id": "a1b2c3d4-...",
"score": 9,
"comment": "Support fixed my issue fast.",
"ai_sentiment": "positive",
"ai_themes": ["support", "speed"],
"ai_churn_risk": "low",
"ai_summary": "Happy with fast support.",
"created_at": "2026-06-18T10:00:00.000Z",
"contact_email": "[email protected]",
"contact_name": "Jordan Lee",
"survey_name": "Q3 relationship survey"
}
}
Get the NPS rollup for your organization, with an optional filter.
| Query | Type | Description |
|---|---|---|
survey_id | UUID | Limit to one survey |
date_from | ISO 8601 | On or after this time |
date_to | ISO 8601 | On or before this time |
curl "https://nps.rallytasks.com/api/v1/nps?survey_id=a1b2c3d4-..." \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": {
"nps": 45,
"total": 88,
"promoters": 52,
"passives": 24,
"detractors": 12,
"promoter_pct": 59.1,
"passive_pct": 27.3,
"detractor_pct": 13.6
}
}
nps comes back null, never a false 0.List your survey contacts, newest first.
| Query | Type | Description |
|---|---|---|
page, limit | integer | See Pagination |
curl https://nps.rallytasks.com/api/v1/contacts \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": [
{
"id": "c3...",
"email": "[email protected]",
"name": "Sam Park",
"status": "pending",
"sent_at": null,
"responded_at": null,
"survey_id": "a1b2c3d4-...",
"survey_name": "Q3 relationship survey"
}
],
"meta": { "page": 1, "limit": 50, "total": 240 }
}
Add or update one contact on a survey. Needs a key with the write permission. Re-posting the same email address (case-insensitive) on the same survey updates that contact instead of adding a duplicate, and returns the same shape.
| Body field | Type | Description |
|---|---|---|
email | string | Required |
survey_id | UUID | Required. Must be a survey in your organization. |
name | string | Optional |
properties | object | Optional custom fields |
curl -X POST https://nps.rallytasks.com/api/v1/contacts \
-H "X-API-Key: rnps_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","survey_id":"a1b2c3d4-...","name":"Sam Park"}'
{
"data": {
"id": "c3...",
"email": "[email protected]",
"name": "Sam Park",
"status": "pending",
"created_at": "2026-06-25T14:00:00.000Z"
}
}
Returns 201 on success. Errors: 400 if
email or survey_id is missing,
403 without the write permission,
404 if the survey is not in your organization.
List your organization's tags, sorted by name.
curl https://nps.rallytasks.com/api/v1/tags \
-H "X-API-Key: rnps_live_your_key_here"
{
"data": [
{ "id": "t1...", "name": "vip", "color": "#35644F", "created_at": "2026-05-01T08:00:00.000Z" }
],
"meta": { "total": 4 }
}
This is API v1. Changes are additive only. We add new endpoints and new response fields, but within v1 we never remove or rename an existing field or change its type. Write your integration to ignore fields it does not recognize. A breaking change would ship as a new version (v2), never as a silent change to v1.