๐Ÿ‘‘ API

Jelly provides a simple REST API that lets you manage labels and contacts programmatically. You can use the API to integrate Jelly with your CRM, build automations, or sync contact data from other tools.

This feature is only available on the Royal Jelly plan

Authentication

All API requests require an API token. You can create one from Settings โ†’ API Tokens in your team settings.

Include your token in the Authorization header of every request:

Authorization: Bearer YOUR_API_TOKEN

API tokens have full access to your team’s data โ€” treat them like passwords. Never share tokens in public repositories or client-side code. Jelly automatically tracks when each token was last used.

Base URL

https://app.letsjelly.com/api

Labels

List Labels

Get all labels for your team, sorted alphabetically.

GET /api/labels

Response:

[
  {
    "id": 1,
    "name": "Bug",
    "color": "cherry"
  },
  {
    "id": 2,
    "name": "Feature",
    "color": "mint"
  }
]

Create Label

Create a new label for your team.

POST /api/labels
Content-Type: application/json
Parameter Required Description
name Yes The label name (must be unique within your team)
color No One of the available colors listed below

Available colors:

gray gray-dark cherry cherry-dark apricot apricot-dark pineapple pineapple-dark gooseberry gooseberry-dark mint mint-dark juniper juniper-dark blueberry blueberry-dark huckleberry huckleberry-dark grape grape-dark plum plum-dark rhubarb rhubarb-dark strawberry strawberry-dark

Example request:

{
  "name": "Important",
  "color": "cherry"
}

Success response:

{
  "id": 3,
  "name": "Important",
  "color": "cherry"
}

Conversation Labels

Add a Label to a Conversation

Apply an existing label to a conversation. The conversation_id is the conversation’s slug (the URL-friendly identifier you see in the address bar).

POST /api/conversations/:conversation_id/labels
Content-Type: application/json
Parameter Required Description
label_id Yes The ID of the label to apply

Example request:

{
  "label_id": 456
}

Success response (201 Created):

Returns all labels currently on the conversation.

[
  {
    "id": 3,
    "name": "Important",
    "color": "cherry"
  }
]

Contacts

Find Contact by Email

Look up an existing contact by their email address.

GET /api/contacts/for_email?email=alice@example.com
Parameter Required Description
email Yes The email address to look up

Success response (200 OK):

{
  "id": "abc123",
  "name": "Alice Smith",
  "email": "alice@example.com",
  "url": "https://app.letsjelly.com/teams/your-team/contacts/abc123"
}

Returns a 404 if no contact is found with that email address.

Create or Update Contact

Create a new contact or update an existing one. If a contact with the given email already exists, it will be updated instead of creating a duplicate.

POST /api/contacts
Content-Type: application/json
Parameter Required Description
email Yes The contact’s email address
name No The contact’s display name
links No An object mapping link titles to URLs. Replaces all existing links when provided.
labels No An array of label names. Replaces all existing contact labels when provided. Labels must already exist on the team.

Example request:

{
  "email": "alice@example.com",
  "name": "Alice Smith",
  "links": {
    "LinkedIn": "https://linkedin.com/in/alice",
    "Website": "https://alice.example.com"
  },
  "labels": ["VIP", "Partner"]
}

Success response (201 Created or 200 OK):

Returns 201 when creating a new contact, or 200 when updating an existing one.

{
  "id": 42,
  "email": "alice@example.com",
  "name": "Alice Smith",
  "links": [
    { "title": "LinkedIn", "url": "https://linkedin.com/in/alice" },
    { "title": "Website", "url": "https://alice.example.com" }
  ],
  "labels": [
    { "name": "VIP", "color": "cherry" },
    { "name": "Partner", "color": "mint" }
  ]
}

When links or labels are included in a request, they replace all existing values for that contact. Omit these fields entirely if you only want to update the name.

Error Handling

All API errors are returned as JSON, never HTML.

Status Meaning Example
401 Missing or invalid API token {"error": "Invalid API token"}
404 Resource or endpoint not found {"error": "Resource not found"}
422 Validation error {"errors": ["Name can't be blank"]}
500 Server error {"error": "Internal server error"}

Examples

curl

# List labels
curl "https://app.letsjelly.com/api/labels" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# Create a label
curl -X POST "https://app.letsjelly.com/api/labels" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Bug", "color": "cherry"}'

# Apply label to a conversation
curl -X POST "https://app.letsjelly.com/api/conversations/my-conversation/labels" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"label_id": 123}'

# Find a contact by email
curl "https://app.letsjelly.com/api/contacts/for_email?email=alice@example.com" \
  -H "Authorization: Bearer YOUR_API_TOKEN"

# Create or update a contact
curl -X POST "https://app.letsjelly.com/api/contacts" \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com", "name": "Alice Smith", "labels": ["VIP"]}'

JavaScript

const API_TOKEN = 'YOUR_API_TOKEN';
const BASE_URL = 'https://app.letsjelly.com/api';

const headers = {
  'Authorization': `Bearer ${API_TOKEN}`,
  'Content-Type': 'application/json'
};

// List labels
async function listLabels() {
  const response = await fetch(`${BASE_URL}/labels`, { headers });
  return response.json();
}

// Create a label
async function createLabel(name, color) {
  const response = await fetch(`${BASE_URL}/labels`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ name, color })
  });
  return response.json();
}

// Find a contact by email
async function findContact(email) {
  const response = await fetch(
    `${BASE_URL}/contacts/for_email?email=${encodeURIComponent(email)}`,
    { headers }
  );
  return response.json();
}

// Create or update a contact
async function upsertContact(email, { name, links, labels } = {}) {
  const response = await fetch(`${BASE_URL}/contacts`, {
    method: 'POST',
    headers,
    body: JSON.stringify({ email, name, links, labels })
  });
  return response.json();
}

Rate Limits

The API currently has no rate limits, but please be respectful and avoid making excessive requests.