Trash
See what was deleted, restore it or delete it for good.
- GETList Trash
/v1/trash - POSTSearch Trash
/v1/trash/search - GETRead a trashed message
/v1/trash/messages/{messageId} - DELETEPermanently delete a message
/v1/trash/messages/{messageId} - POSTRestore a message
/v1/trash/messages/{messageId}/restore - GETRead a trashed thread
/v1/trash/threads/{threadId} - DELETEPermanently delete a thread
/v1/trash/threads/{threadId} - POSTRestore a thread
/v1/trash/threads/{threadId}/restore - DELETEPermanently delete a source
/v1/trash/sources/{sourceId} - POSTRestore a source
/v1/trash/sources/{sourceId}/restore
List Trash
GET/v1/trash
Lists the messages, threads and sources in Trash, starting with whichever will be deleted forever soonest. For the next page, pass nextCursor as cursor.
AuthAPI token
Query parameters
typestringmessagethreadsourcecontentLists one kind of item:
messagefor messages that aren't in a thread,thread,source, orcontentfor messages and threads together. Leave it out for everything.limitinteger · 1–100 · default 50The most items to return in one page.
cursorstringThe
nextCursorfrom the previous page. Keep the other filters the same.
Responses
- 200
Items in Trash, soonest to be deleted forever first
Response fields
itemslist of objectsRequiredFields
One of
TrashMessageListItemtypestringRequiredmessageidstringRequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequireddeletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
Or
TrashThreadListItemtypestringRequiredthreadidstring · starts with thr_RequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · up to 120 charactersRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredreceivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectRequiredFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
deletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
Or
TrashSourceListItemtypestringRequiredsourceidstringRequiredsourceobjectRequiredFields
sourceIdstring · starts with src_RequiredThe source's permanent ID, which
POST/v1/takes assend sourceand lists take assourceId.labelstringRequiredkindstringRequiredsystemcustomsystemfor Inbox, which every account has, andcustomfor sources you create.statusstringRequiredactivemuteddisabledWhether the source is muted or disabled. These are separate: a source that's both shows
disabled, and showsmutedagain once it's enabled.iconSymbolstring · can be nullRequirediconColorstring · can be nullRequiredredorangeyellowgreentealblueindigopurplepinkThe color of the source's icon tile, or of the tile showing its initial.
nulluses a neutral tile. AlwaysnullwhileimageUrlis set.imageUrlstring · can be nullRequiredThe source's image: an
https:/URL, or a/ /api/path for an image uploaded to Ripple.source-images/ {imageId} unreadCountinteger · at least 0RequiredHow many unread messages the source has, leaving out archived and snoozed ones. A thread counts once, however many messages it holds.
importantUnreadCountinteger · at least 0RequiredHow many of the messages in
unreadCounthave the alertimportantnow. A thread uses its latest message's alert.quietUnreadCountinteger · at least 0RequiredHow many of the messages in
unreadCounthave the alertquietnow. Messages sent without an alert count asstandard, so they're in neither this count norimportantUnreadCount.snoozedCountinteger · at least 0RequiredHow many of the source's messages are snoozed, read or unread. A thread counts once. Snoozed messages are left out of
unreadCount, and archived or deleted messages are never snoozed.hasMessagesbooleanRequiredtrueif the source has any messages outside Trash, including archived and snoozed ones.defaultAlertstring · can be nullRequiredquietstandardimportantdefaultSoundstring · can be nullRequiredhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound this source's new messages play in Ripple unless a message names its own; null plays the account's sound. Turning sounds off in Ripple's settings silences every source.
defaultLinkstring · URL · starts with https:// · can be nullRequiredAn
https:/link that messages get when they're sent without/ link.mutedAtstring · date and time · can be nullRequireddisabledAtstring · date and time · can be nullRequiredautoContainedUntilstring · date and time · can be nullRequiredWhen the source's 15-minute pause for going over its send limit ends or ended. Until then, its sends return
429 source_rate_limited.nullif it has never been paused.lastMessageAtstring · date and time · can be nullRequiredWhen the source last received a message, or
nullif it never has. Moving, archiving or deleting messages doesn't change it, and a new message doesn't change the source'supdatedAt.createdAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
deletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
totalinteger · at least 0RequiredHow many items match the filters, across all pages.
nextCursorstring · can be nullRequiredPass this as
cursorto get the next page.nullon the last page.
- 400
typeisn't valid (invalid_trash_item_type), or the cursor came from a different list (invalid_cursor) - 401
The request has no valid API token or sign-in (
unauthorized) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash \
-H "Authorization: Bearer $RIPPLE_KEY"const response = await fetch("https://api.ripplepush.com/v1/trash", {
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
},
});
console.log(response.status, await response.text());import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash",
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
},
method="GET",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"items": [
{
"type": "thread",
"id": "thr_e91d5ec03ea9489d89a302645ccca63b",
"messageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"title": "Release progress",
"preview": "Build completed.",
"alert": "standard",
"firstReceivedAt": "2026-10-03T13:31:07.219Z",
"receivedAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z",
"readAt": "2026-10-03T13:31:07.522Z",
"archivedAt": null,
"revision": 4,
"snooze": null,
"thread": {
"id": "thr_e91d5ec03ea9489d89a302645ccca63b",
"title": "Release progress",
"latestMessageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"latestMessageNumber": 1,
"messageCount": 1,
"lastSeenMessageNumber": 1,
"unseenMessageCount": 0,
"firstUnseenMessageNumber": null,
"manuallyUnread": false,
"createdAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z"
},
"deletedAt": "2026-10-03T13:31:07.788Z",
"purgeAt": "2026-11-02T13:31:07.788Z"
}
],
"total": 1,
"nextCursor": null
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}Items in Trash, soonest to be deleted forever first
Search Trash
POST/v1/trash/search
Searches the messages and threads in Trash that can still be restored. Matching, ranking, filters, corrections and pages work as in POST /v1/, except there's no in. Sources in Trash are not searched.
AuthAPI token
Body
querystring · up to 500 charactersThe words to search for, up to 64. Every word must match, and the last can be the start of a word, so
deplfindsdeploy. Put the whole query in double quotes to find the words together in one field. Words with digits or symbols, such as IDs, paths and percentages, match exactly. Typo correction only runs on unquoted searches of 12 words or fewer. Leave it out or blank to get everything the filters allow, newest first.unreadbooleantruefinds only unread messages andfalseonly read ones. Leave it out for both. A thread is read or unread as a whole, as its result'sreadAtshows, whichever of its messages matched.sourceIdslist of strings · 1–100 itemsSearches only these sources, by ID. Use Inbox's ID for Inbox.
afterstring · date and timeFinds only messages whose
receivedAtis at or after this time.beforestring · date and timeFinds only messages whose
receivedAtis before this time. If you also sendafter, this must be later.sortstringrelevancenewestrelevance, the default, ranks by how well each result matches, then newest first byreceivedAt.newestsorts byreceivedAtalone and places a thread at its newest matching message. A search without a query is always newest first.alertstringquietstandardimportantFinds only messages whose alert is now this one. Messages sent without an alert count as
standard.limitinteger · 1–100 · default 50The most results to return in one page.
cursorstring · 1–200 charactersThe
nextCursorfrom the previous page. Keep the query and filters the same.
Responses
- 200
One page of ranked results from Trash
Response fields
itemslist of objectsRequiredFields
itemobjectRequiredFields
One of
TrashMessageListItemtypestringRequiredmessageidstringRequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequireddeletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
Or
TrashThreadListItemtypestringRequiredthreadidstring · starts with thr_RequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · up to 120 charactersRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredreceivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectRequiredFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
deletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
excerptobjectRequiredPlain text from the matching message's body, around the first match. It never contains markup.
Fields
textstringRequiredtruncatedStartbooleanRequiredtruncatedEndbooleanRequired
nextCursorstring · can be nullRequiredPass this as
cursorfor the next page. It'snullwhen there are no more matches, or when the search has reached its limit: one search returns at most 1,000 results plus one page. Checktruncatedbefore you treat the results as complete.truncatedbooleanRequiredtruewhen more matches exist but the search can't return another page, sonextCursorisnull. Narrow the query or filters to reach them.falsedoesn't mean you have everything whilenextCursoris still set. This is separate fromcoverage.correctionslist of objectsRequiredFields
originalstringRequiredalternativeslist of strings · 1–4 itemsRequired
coveragestringRequiredcompleteindexingindexingwhile Ripple is building or rebuilding the search index, so results cover only the messages indexed so far.completemeans every message is indexed. It says nothing about the result limit; checktruncatedfor that.
- 400
The request is malformed or its filters conflict (
invalid_search_request), the cursor came from a different search (invalid_cursor), or the query has more than 64 words (too_many_terms) - 401
The request has no valid API token or sign-in (
unauthorized) - 503
Search is at capacity (
search_busy), the search took too long (search_timeout), or Ripple is busy (database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash/search \
-H "Authorization: Bearer $RIPPLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "Build"
}'const response = await fetch("https://api.ripplepush.com/v1/trash/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "Build",
}),
});
console.log(response.status, await response.text());import json
import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/search",
data=json.dumps({
"query": "Build",
}).encode(),
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"items": [],
"nextCursor": null,
"truncated": false,
"corrections": [],
"coverage": "complete"
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}One page of ranked results from Trash
Read a trashed message
GET/v1/trash/messages/{messageId}
Returns a message in Trash and its history. For a message from a thread in Trash, you get the whole thread instead, as GET /v1/ returns it.
AuthAPI token
Path parameters
messageIdstringRequired
Responses
- 200
The message in Trash
Response fields
One of
TrashStandaloneMessageDetailResponseitemobjectRequiredFields
typestringRequiredmessageidstringRequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequireddeletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequiredbodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequired
timelinelist of objectsRequiredFields
eventIdstringRequiredeventTypestringRequiredcreatedAtstring · date and timeRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
messageIdstringmessageNumberinteger · at least 1metadataobject · can be nullFields
threadTitlestring · can be nullThe thread's title when the message arrived, or
nullfor a message that isn't in a thread.snoozeIdstringgenerationinteger · at least 1returnAtstring · date and timetimeZonestringpreviousReturnAtstring · date and timepreviousTimeZonestringalertstringquietstandardimportantfromstring · can be nullquietstandardimportanttostringquietstandardimportantfromSourceobjectThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
toSourceobjectThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
Or
TrashThreadDetailResponseitemobjectRequiredFields
typestringRequiredthreadidstring · starts with thr_RequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · up to 120 charactersRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredreceivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectRequiredFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
deletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
latestMessageobjectRequiredFields
messageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.bodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequiredalertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
sourceStatestringRequiredactivetrashdeletedsourceDeletedAtstring · date and time · can be nullRequired
messageslist of objectsRequiredFields
messageIdstringRequiredmessageNumberinteger · at least 1RequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredbodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequiredalertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
receivedAtstring · date and timeRequired
activitylist of objectsRequiredFields
eventIdstringRequiredeventTypestringRequiredarchivedunarchivedsnoozedsnooze_changedsnooze_canceledalert_changedcreatedAtstring · date and timeRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
messageIdstringRequiredmessageNumberinteger · at least 1Requiredmetadataobject · can be nullRequired
nextCursorstring · can be nullRequiredPass this as
cursorfor older messages.nullon the last page.
- 401
The request has no valid API token or sign-in (
unauthorized) - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID \
-H "Authorization: Bearer $RIPPLE_KEY"const response = await fetch("https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID", {
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
},
});
console.log(response.status, await response.text());import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID",
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
},
method="GET",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"item": {
"type": "message",
"id": "msg_0575320416fe4544b646044392618fbd",
"preview": "Version 1.2 is ready.",
"messageId": "msg_0575320416fe4544b646044392618fbd",
"source": {
"kind": "custom",
"id": "src_65ab57d4851f4e828dad9e4c29ea0f48",
"label": "Finished builds"
},
"title": "Deployment complete",
"body": "Version 1.2 is ready.",
"link": null,
"linkTitle": null,
"alert": "quiet",
"firstReceivedAt": "2026-10-03T13:31:07.201Z",
"receivedAt": "2026-10-03T13:31:07.201Z",
"updatedAt": "2026-10-03T13:31:07.201Z",
"readAt": null,
"archivedAt": null,
"revision": 9,
"snooze": null,
"deletedAt": "2026-10-03T13:31:07.627Z",
"purgeAt": "2026-11-02T13:31:07.627Z"
},
"timeline": [
{
"eventId": "evt_7b747256f28f4f93970ef0331bc84912",
"eventType": "deleted",
"createdAt": "2026-10-03T13:31:07.627Z",
"source": {
"kind": "custom",
"id": "src_65ab57d4851f4e828dad9e4c29ea0f48",
"label": "Finished builds"
},
"metadata": null,
"messageId": "msg_0575320416fe4544b646044392618fbd"
},
{
"eventId": "evt_893f5667d60d4d74be8102a951caf37c",
"eventType": "source_changed",
"createdAt": "2026-10-03T13:31:07.445Z",
"source": {
"kind": "custom",
"id": "src_65ab57d4851f4e828dad9e4c29ea0f48",
"label": "Finished builds"
},
"metadata": {
"toSource": {
"id": "src_65ab57d4851f4e828dad9e4c29ea0f48",
"kind": "custom",
"label": "Finished builds"
},
"fromSource": {
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"kind": "custom",
"label": "Deploy notifications"
}
},
"messageId": "msg_0575320416fe4544b646044392618fbd"
},
{
"eventId": "evt_c9220cf9e41f47cf85a258aa86751d38",
"eventType": "snooze_canceled",
"createdAt": "2026-10-03T13:31:07.428Z",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"metadata": {
"alert": "quiet",
"returnAt": "2026-10-03T14:31:07.364Z",
"snoozeId": "snz_b4b4e9c6740d4c78b4b73e6115c1c335",
"timeZone": "UTC",
"generation": 2
},
"messageId": "msg_0575320416fe4544b646044392618fbd"
},
{
"eventId": "evt_4584fb874c8445b3883c71458835b633",
"eventType": "snoozed",
"createdAt": "2026-10-03T13:31:07.417Z",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"metadata": {
"alert": "quiet",
"returnAt": "2026-10-03T14:31:07.364Z",
"snoozeId": "snz_b4b4e9c6740d4c78b4b73e6115c1c335",
"timeZone": "UTC",
"generation": 1
},
"messageId": "msg_0575320416fe4544b646044392618fbd"
},
{
"eventId": "evt_51c8563239c544609b7e08f6dc319ac8",
"eventType": "accepted",
"createdAt": "2026-10-03T13:31:07.201Z",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"metadata": {
"alert": "standard",
"threadTitle": null
},
"messageId": "msg_0575320416fe4544b646044392618fbd"
}
]
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}The message in Trash
Permanently delete a message
DELETE/v1/trash/messages/{messageId}
Deletes a message in Trash forever; this can't be undone. If the message is from a thread in Trash, the whole thread is deleted. Send no body.
AuthAPI token
Path parameters
messageIdstringRequired
Query parameters
deletedAtstring · date and timeThe item's
deletedAtfrom Trash, so the request acts only if the item is still the one you saw. Omit it to act on whatever is in Trash now.
Responses
- 204
Message permanently deleted
- 400
deletedAtis not a valid time (invalid_trash_item_mutation), or the request sent a body (unexpected_body) - 401
The request has no valid API token or sign-in (
unauthorized) - 403
The API token is read-only (
read_only_token), so it can only list, read and search. - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl -X DELETE https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID \
-H "Authorization: Bearer $RIPPLE_KEY"const response = await fetch("https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
},
});
console.log(response.status, await response.text());import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID",
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
},
method="DELETE",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}{
"code": "read_only_token",
"message": "This API token can only read."
}The request has no valid API token or sign-in (`unauthorized`)
Restore a message
POST/v1/trash/messages/{messageId}/restore
Takes a message out of Trash with its read and archived state as they were, even while its source is in Trash. If the message is from a thread in Trash, the whole thread is restored.
AuthAPI token
Path parameters
messageIdstringRequired
BodyOptional
deletedAtstring · date and timeThe item's
deletedAtfrom Trash, so the request acts only if the item is still the one you saw. Omit it to act on whatever is in Trash now.
Responses
- 200
Message restored
Response fields
messageobjectRequiredFields
messageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.bodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequiredalertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
sourceStatestringactivetrashdeletedsourceDeletedAtstring · date and time · can be null
- 400
deletedAtis not a valid time (invalid_trash_item_mutation) - 401
The request has no valid API token or sign-in (
unauthorized) - 403
The API token is read-only (
read_only_token), so it can only list, read and search. - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 409
A newer thread with the same title is already active in the source (
thread_restore_conflict) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID/restore \
-H "Authorization: Bearer $RIPPLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"deletedAt": "2026-10-03T13:31:07.627Z"
}'const response = await fetch("https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID/restore", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
deletedAt: "2026-10-03T13:31:07.627Z",
}),
});
console.log(response.status, await response.text());import json
import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/messages/MESSAGE_ID/restore",
data=json.dumps({
"deletedAt": "2026-10-03T13:31:07.627Z",
}).encode(),
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"message": {
"messageId": "msg_0575320416fe4544b646044392618fbd",
"source": {
"kind": "custom",
"id": "src_65ab57d4851f4e828dad9e4c29ea0f48",
"label": "Finished builds"
},
"title": "Deployment complete",
"preview": "Version 1.2 is ready.",
"body": "Version 1.2 is ready.",
"link": null,
"linkTitle": null,
"alert": "quiet",
"firstReceivedAt": "2026-10-03T13:31:07.201Z",
"receivedAt": "2026-10-03T13:31:07.201Z",
"updatedAt": "2026-10-03T13:31:07.201Z",
"readAt": null,
"archivedAt": null,
"revision": 10,
"snooze": null,
"sourceDeletedAt": null,
"sourceState": "active"
}
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}{
"code": "read_only_token",
"message": "This API token can only read."
}Message restored
Read a trashed thread
GET/v1/trash/threads/{threadId}
Returns a thread in Trash and a page of its messages, newest first. For the next page, pass nextCursor as cursor.
AuthAPI token
Path parameters
threadIdstring · starts with thr_Required
Query parameters
limitinteger · 1–100 · default 50The most items to return in one page.
cursorstringThe
nextCursorfrom the previous page. Keep the other filters the same.
Responses
- 200
The thread in Trash and one page of its messages
Response fields
itemobjectRequiredFields
typestringRequiredthreadidstring · starts with thr_RequiredmessageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · up to 120 charactersRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.alertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredreceivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectRequiredFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
deletedAtstring · date and timeRequiredpurgeAtstring · date and timeRequired
latestMessageobjectRequiredFields
messageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.bodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequiredalertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
sourceStatestringRequiredactivetrashdeletedsourceDeletedAtstring · date and time · can be nullRequired
messageslist of objectsRequiredFields
messageIdstringRequiredmessageNumberinteger · at least 1RequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredbodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequiredalertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
receivedAtstring · date and timeRequired
activitylist of objectsRequiredFields
eventIdstringRequiredeventTypestringRequiredarchivedunarchivedsnoozedsnooze_changedsnooze_canceledalert_changedcreatedAtstring · date and timeRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
messageIdstringRequiredmessageNumberinteger · at least 1Requiredmetadataobject · can be nullRequired
nextCursorstring · can be nullRequiredPass this as
cursorfor older messages.nullon the last page.
- 400
The cursor came from a different list (
invalid_cursor) - 401
The request has no valid API token or sign-in (
unauthorized) - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash/threads/thr_abc123 \
-H "Authorization: Bearer $RIPPLE_KEY"const response = await fetch("https://api.ripplepush.com/v1/trash/threads/thr_abc123", {
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
},
});
console.log(response.status, await response.text());import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/threads/thr_abc123",
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
},
method="GET",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"item": {
"type": "thread",
"id": "thr_e91d5ec03ea9489d89a302645ccca63b",
"messageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"title": "Release progress",
"preview": "Build completed.",
"alert": "standard",
"firstReceivedAt": "2026-10-03T13:31:07.219Z",
"receivedAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z",
"readAt": "2026-10-03T13:31:07.522Z",
"archivedAt": null,
"revision": 2,
"snooze": null,
"thread": {
"id": "thr_e91d5ec03ea9489d89a302645ccca63b",
"title": "Release progress",
"latestMessageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"latestMessageNumber": 1,
"messageCount": 1,
"lastSeenMessageNumber": 1,
"unseenMessageCount": 0,
"firstUnseenMessageNumber": null,
"manuallyUnread": false,
"createdAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z"
},
"deletedAt": "2026-10-03T13:31:07.734Z",
"purgeAt": "2026-11-02T13:31:07.734Z"
},
"latestMessage": {
"messageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"title": null,
"preview": "Build completed.",
"body": "Build completed.",
"link": null,
"linkTitle": null,
"alert": "standard",
"firstReceivedAt": "2026-10-03T13:31:07.219Z",
"receivedAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z",
"readAt": "2026-10-03T13:31:07.522Z",
"archivedAt": null,
"revision": 2,
"snooze": null,
"thread": {
"id": "thr_e91d5ec03ea9489d89a302645ccca63b",
"title": "Release progress",
"latestMessageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"latestMessageNumber": 1,
"messageCount": 1,
"lastSeenMessageNumber": 1,
"unseenMessageCount": 0,
"firstUnseenMessageNumber": null,
"manuallyUnread": false,
"createdAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z"
},
"sourceDeletedAt": null,
"sourceState": "active"
},
"messages": [
{
"messageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"messageNumber": 1,
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"title": null,
"body": "Build completed.",
"link": null,
"linkTitle": null,
"alert": "standard",
"receivedAt": "2026-10-03T13:31:07.219Z"
}
],
"activity": [],
"nextCursor": null
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}The thread in Trash and one page of its messages
Permanently delete a thread
DELETE/v1/trash/threads/{threadId}
Deletes a thread in Trash and all its messages forever; this can't be undone. Send no body.
AuthAPI token
Path parameters
threadIdstring · starts with thr_Required
Query parameters
deletedAtstring · date and timeThe item's
deletedAtfrom Trash, so the request acts only if the item is still the one you saw. Omit it to act on whatever is in Trash now.
Responses
- 204
Thread permanently deleted
- 400
deletedAtis not a valid time (invalid_trash_item_mutation), or the request sent a body (unexpected_body) - 401
The request has no valid API token or sign-in (
unauthorized) - 403
The API token is read-only (
read_only_token), so it can only list, read and search. - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl -X DELETE https://api.ripplepush.com/v1/trash/threads/thr_abc123 \
-H "Authorization: Bearer $RIPPLE_KEY"const response = await fetch("https://api.ripplepush.com/v1/trash/threads/thr_abc123", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
},
});
console.log(response.status, await response.text());import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/threads/thr_abc123",
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
},
method="DELETE",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}{
"code": "read_only_token",
"message": "This API token can only read."
}The request has no valid API token or sign-in (`unauthorized`)
Restore a thread
POST/v1/trash/threads/{threadId}/restore
Takes a whole thread out of Trash with its messages and history. It's never merged into a newer thread with the same title in the same source; if one is active, you get 409 thread_restore_conflict.
AuthAPI token
Path parameters
threadIdstring · starts with thr_Required
BodyOptional
deletedAtstring · date and timeThe item's
deletedAtfrom Trash, so the request acts only if the item is still the one you saw. Omit it to act on whatever is in Trash now.
Responses
- 200
Thread restored
Response fields
messageobjectRequiredFields
messageIdstringRequiredsourceobjectRequiredThe source a message belongs to. Inbox is the built-in
systemsource; sources you create arecustom.Fields
kindstringRequiredsystemcustomidstring · starts with src_RequiredlabelstringRequired
titlestring · can be nullRequiredpreviewstringRequiredThe body as one line of plain text, without Markdown syntax or HTML tags, after a first line that repeats the title. Past 280 UTF-16 code units it ends with
…. Empty when nothing follows the title.bodystring · can be nullRequiredlinkstring · URL · can be nullRequiredlinkTitlestring · up to 64 characters · can be nullRequiredalertstring · can be nullRequiredquietstandardimportantsoundstring · can be nullhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound the sender chose for this message. Null when it plays its source's sound or the account's.
firstReceivedAtstring · date and timeRequiredWhen Ripple first received the message. It never changes.
receivedAtstring · date and timeRequiredupdatedAtstring · date and timeRequiredrevisioninteger · at least 1Requiredsnoozeobject · can be nullRequiredFields
snoozeIdstringRequiredreturnAtstring · date and timeRequiredtimeZonestringRequiredalertstringRequiredquietstandardimportantgenerationinteger · at least 1RequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
readAtstring · date and time · can be nullRequiredarchivedAtstring · date and time · can be nullRequiredthreadobjectFields
idstring · starts with thr_Requiredtitlestring · up to 120 charactersRequiredlatestMessageIdstringRequiredlatestMessageNumberinteger · at least 1RequiredmessageCountinteger · at least 1RequiredlastSeenMessageNumberinteger · at least 0RequiredunseenMessageCountinteger · at least 0RequiredfirstUnseenMessageNumberinteger · at least 1 · can be nullRequiredmanuallyUnreadbooleanRequiredcreatedAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
sourceStatestringactivetrashdeletedsourceDeletedAtstring · date and time · can be null
- 400
deletedAtis not a valid time (invalid_trash_item_mutation) - 401
The request has no valid API token or sign-in (
unauthorized) - 403
The API token is read-only (
read_only_token), so it can only list, read and search. - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 409
A newer thread with the same title is already active in the source (
thread_restore_conflict) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash/threads/thr_abc123/restore \
-H "Authorization: Bearer $RIPPLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"deletedAt": "2026-10-03T13:31:07.627Z"
}'const response = await fetch("https://api.ripplepush.com/v1/trash/threads/thr_abc123/restore", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
deletedAt: "2026-10-03T13:31:07.627Z",
}),
});
console.log(response.status, await response.text());import json
import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/threads/thr_abc123/restore",
data=json.dumps({
"deletedAt": "2026-10-03T13:31:07.627Z",
}).encode(),
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"message": {
"messageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"source": {
"kind": "custom",
"id": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications"
},
"title": null,
"preview": "Build completed.",
"body": "Build completed.",
"link": null,
"linkTitle": null,
"alert": "standard",
"firstReceivedAt": "2026-10-03T13:31:07.219Z",
"receivedAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z",
"readAt": "2026-10-03T13:31:07.522Z",
"archivedAt": null,
"revision": 3,
"snooze": null,
"thread": {
"id": "thr_e91d5ec03ea9489d89a302645ccca63b",
"title": "Release progress",
"latestMessageId": "msg_2fe56d9f692c4febb604bb2c2c884ae7",
"latestMessageNumber": 1,
"messageCount": 1,
"lastSeenMessageNumber": 1,
"unseenMessageCount": 0,
"firstUnseenMessageNumber": null,
"manuallyUnread": false,
"createdAt": "2026-10-03T13:31:07.219Z",
"updatedAt": "2026-10-03T13:31:07.219Z"
},
"sourceDeletedAt": null,
"sourceState": "active"
}
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}{
"code": "read_only_token",
"message": "This API token can only read."
}Thread restored
Permanently delete a source
DELETE/v1/trash/sources/{sourceId}
Deletes a source in Trash forever; this can't be undone. Its messages stay where they are. Send no body.
AuthAPI token
Path parameters
sourceIdstring · starts with src_Required
Query parameters
deletedAtstring · date and timeThe item's
deletedAtfrom Trash, so the request acts only if the item is still the one you saw. Omit it to act on whatever is in Trash now.
Responses
- 204
Source permanently deleted
- 400
deletedAtis not a valid time (invalid_trash_item_mutation), or the request sent a body (unexpected_body) - 401
The request has no valid API token or sign-in (
unauthorized) - 403
The API token is read-only (
read_only_token), so it can only list, read and search. - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl -X DELETE https://api.ripplepush.com/v1/trash/sources/src_abc123 \
-H "Authorization: Bearer $RIPPLE_KEY"const response = await fetch("https://api.ripplepush.com/v1/trash/sources/src_abc123", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
},
});
console.log(response.status, await response.text());import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/sources/src_abc123",
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
},
method="DELETE",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}{
"code": "read_only_token",
"message": "This API token can only read."
}The request has no valid API token or sign-in (`unauthorized`)
Restore a source
POST/v1/trash/sources/{sourceId}/restore
Takes a source out of Trash. Any of its messages in Trash stay there until you restore them.
AuthAPI token
Path parameters
sourceIdstring · starts with src_Required
BodyOptional
deletedAtstring · date and timeThe item's
deletedAtfrom Trash, so the request acts only if the item is still the one you saw. Omit it to act on whatever is in Trash now.
Responses
- 200
Source restored
Response fields
sourceobjectRequiredFields
sourceIdstring · starts with src_RequiredThe source's permanent ID, which
POST/v1/takes assend sourceand lists take assourceId.labelstringRequiredkindstringRequiredsystemcustomsystemfor Inbox, which every account has, andcustomfor sources you create.statusstringRequiredactivemuteddisabledWhether the source is muted or disabled. These are separate: a source that's both shows
disabled, and showsmutedagain once it's enabled.iconSymbolstring · can be nullRequirediconColorstring · can be nullRequiredredorangeyellowgreentealblueindigopurplepinkThe color of the source's icon tile, or of the tile showing its initial.
nulluses a neutral tile. AlwaysnullwhileimageUrlis set.imageUrlstring · can be nullRequiredThe source's image: an
https:/URL, or a/ /api/path for an image uploaded to Ripple.source-images/ {imageId} unreadCountinteger · at least 0RequiredHow many unread messages the source has, leaving out archived and snoozed ones. A thread counts once, however many messages it holds.
importantUnreadCountinteger · at least 0RequiredHow many of the messages in
unreadCounthave the alertimportantnow. A thread uses its latest message's alert.quietUnreadCountinteger · at least 0RequiredHow many of the messages in
unreadCounthave the alertquietnow. Messages sent without an alert count asstandard, so they're in neither this count norimportantUnreadCount.snoozedCountinteger · at least 0RequiredHow many of the source's messages are snoozed, read or unread. A thread counts once. Snoozed messages are left out of
unreadCount, and archived or deleted messages are never snoozed.hasMessagesbooleanRequiredtrueif the source has any messages outside Trash, including archived and snoozed ones.defaultAlertstring · can be nullRequiredquietstandardimportantdefaultSoundstring · can be nullRequiredhoparcadeclinkdropechoglowknockliftsparkleswooptickwaveThe sound this source's new messages play in Ripple unless a message names its own; null plays the account's sound. Turning sounds off in Ripple's settings silences every source.
defaultLinkstring · URL · starts with https:// · can be nullRequiredAn
https:/link that messages get when they're sent without/ link.mutedAtstring · date and time · can be nullRequireddisabledAtstring · date and time · can be nullRequiredautoContainedUntilstring · date and time · can be nullRequiredWhen the source's 15-minute pause for going over its send limit ends or ended. Until then, its sends return
429 source_rate_limited.nullif it has never been paused.lastMessageAtstring · date and time · can be nullRequiredWhen the source last received a message, or
nullif it never has. Moving, archiving or deleting messages doesn't change it, and a new message doesn't change the source'supdatedAt.createdAtstring · date and timeRequiredupdatedAtstring · date and timeRequired
- 400
deletedAtis not a valid time (invalid_trash_item_mutation) - 401
The request has no valid API token or sign-in (
unauthorized) - 403
The API token is read-only (
read_only_token), so it can only list, read and search. - 404
The item is not in Trash, or no longer the one
deletedAtnames (not_found) - 503
Ripple is busy right now (
database_busy). Wait the number of seconds inRetry-After, then try again.
curl https://api.ripplepush.com/v1/trash/sources/src_abc123/restore \
-H "Authorization: Bearer $RIPPLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"deletedAt": "2026-10-03T13:31:07.627Z"
}'const response = await fetch("https://api.ripplepush.com/v1/trash/sources/src_abc123/restore", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RIPPLE_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
deletedAt: "2026-10-03T13:31:07.627Z",
}),
});
console.log(response.status, await response.text());import json
import os
import urllib.request
request = urllib.request.Request(
"https://api.ripplepush.com/v1/trash/sources/src_abc123/restore",
data=json.dumps({
"deletedAt": "2026-10-03T13:31:07.627Z",
}).encode(),
headers={
"Authorization": "Bearer " + os.environ["RIPPLE_KEY"],
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(request, timeout=30) as response:
print(response.status, response.read().decode()){
"source": {
"sourceId": "src_aa960551ca5b4ec68f82747db1f75a3b",
"label": "Deploy notifications",
"kind": "custom",
"status": "active",
"iconSymbol": null,
"iconColor": "blue",
"imageUrl": null,
"unreadCount": 0,
"importantUnreadCount": 0,
"quietUnreadCount": 0,
"snoozedCount": 0,
"hasMessages": false,
"defaultAlert": "quiet",
"defaultSound": null,
"defaultLink": "https://example.com/builds",
"mutedAt": null,
"disabledAt": null,
"autoContainedUntil": null,
"lastMessageAt": null,
"createdAt": "2026-10-03T13:31:07.176Z",
"updatedAt": "2026-10-03T13:31:07.831Z"
}
}{
"code": "unauthorized",
"message": "Send a valid API token, or sign in."
}{
"code": "read_only_token",
"message": "This API token can only read."
}Source restored