RallyNPS API v1

Read your surveys, responses, and NPS. Send surveys to your contacts.

Overview

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.

Want events pushed to you instead of polling? See the Webhooks docs for real-time delivery of response, detractor, and survey events.

Authentication

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.

Rate Limits

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.

Errors

Every error uses the same envelope: a JSON object with a single error string.

{
  "error": "Survey not found"
}
StatusMeaning
400Bad request. A required field is missing or invalid.
401Unauthorized. The X-API-Key header is missing, unknown, revoked, or expired.
403Forbidden. The key lacks the write permission a writing endpoint needs, or a send would exceed your monthly send limit.
404Not found. The path, survey, response, or contact does not exist in your organization.
429Too many requests. The per-key rate limit was exceeded.

Pagination

List endpoints accept page (default 1) and limit (default 50, max 200) query parameters, and return a meta object with page, limit, and total.

Endpoints

GET /api/v1/surveys

List your surveys, newest first.

QueryTypeDescription
pageintegerPage number (default 1)
limitintegerRows 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 /api/v1/surveys/:id

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
    }
  }
}
No responses yet? nps and avg_score come back null, never a false 0.

POST /api/v1/surveys/:id/send

Send a survey to a list of contacts. Needs a key with the write permission.

Behavior as of July 2026: each contact is emailed inline through the dedup layer, the same duplicate protection the app uses, then reported back with its own final 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 fieldTypeDescription
contactsarrayRequired. 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.

statusMeaning
"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
}

GET /api/v1/responses

List responses across your surveys, newest first.

QueryTypeDescription
survey_idUUIDLimit to one survey
categorystringpromoter, passive, or detractor
date_fromISO 8601On or after this time
date_toISO 8601On or before this time
searchstringMatch on comment, contact email, or contact name
page, limitintegerSee 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 Insight fields (ai_sentiment, ai_themes, ai_churn_risk, ai_summary) are filled only when AI is enabled for your organization.

GET /api/v1/responses/:id

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 /api/v1/nps

Get the NPS rollup for your organization, with an optional filter.

QueryTypeDescription
survey_idUUIDLimit to one survey
date_fromISO 8601On or after this time
date_toISO 8601On 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
  }
}
No scored responses? nps comes back null, never a false 0.

GET /api/v1/contacts

List your survey contacts, newest first.

QueryTypeDescription
page, limitintegerSee 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 }
}

POST /api/v1/contacts

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 fieldTypeDescription
emailstringRequired
survey_idUUIDRequired. Must be a survey in your organization.
namestringOptional
propertiesobjectOptional 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.

GET /api/v1/tags

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 }
}

Versioning

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.