
iMessage has no built-in "awaiting reply" flag. A promise sent on Monday scrolls out of view by Tuesday, and high-volume threads bury single-line commitments completely. The fix is a small state machine you run against your message history: classify every promise-bearing message, check each one for a resolution signal, and send a follow-up only when the commitment is still open. Figment's iMessage API gives you the search, history, and send endpoints to build exactly that.
Design guide. The endpoint shapes below reflect the Figment iMessage REST interface. Use them against your own configured, approved connection after completing setup.
Email clients give you flags, snooze, and "awaiting reply" filters. iMessage gives you a scrolling conversation. There is no native concept of thread status, no unresolved-action badge, and no way to star a specific message across contacts without a third-party tool.
The result is predictable: you send "Can you confirm the quote by Friday?", get pulled into another conversation, and Friday passes silently. The original message is still there — it is just several hundred messages deep and invisible unless you search for it.
Programmatic follow-up tracking changes the loop. You search your message history on a schedule, classify what you find, and act on the classification.
Each row is a state in a simple finite state machine. Your code assigns a state to every tracked message, identified by its guid from the API response, and re-evaluates it on each poll cycle. Store those guid values in your own database alongside the conversation's chatGuid and your deadline timestamp.
| State | What it means | Signal phrases | Resolution check | Action |
|---|---|---|---|---|
| Sent | Commitment dispatched; no reply yet | "Can you…", "Please send…", "Let me know if…", "Will you…" | Has any message with isFromMe: false appeared after this guid in the thread? | Move to Awaiting on next poll |
| Awaiting | Reply window open; no response detected | (same thread, no inbound reply) | Re-fetch thread and re-check | Wait; escalate to Lapsed after your deadline |
| Ambiguous | A reply arrived but resolution is unclear | "Maybe", "I'll try", "possibly", "working on it" | Re-check on next poll; optionally run a classifier | Hold; do not send a follow-up; check again at next interval |
| Resolved | Commitment confirmed by a clear reply | "Done", "Sent", "Yes", "Confirmed", "Here it is" | Mark closed; stop polling | Log and archive |
| Lapsed | Your deadline passed with no resolution | (no Resolved or Ambiguous reply by deadline) | Has a manual override been set? | Trigger follow-up send with a persisted idempotency key |
Search message history for commitment language using the search endpoint. The messages:read scope covers both search and thread history — no separate search scope is needed. Build the URL with a proper URL encoder; a raw + character in a query string decodes as a space:
GET https://figment.so/api/apple-connect/v1/connections/con_xxxx/messages/search
?query=can%20you%20confirm
&limit=50
Authorization: Bearer ACCESS_TOKEN
Run a separate query for each phrase pattern that matters — "please%20send", "let%20me%20know", "will%20you". The response envelope is {data:[...], mode, hasMore, nextOffset, historyComplete, scope}. Paginate using hasMore and nextOffset; do not guess a sentinel count.
Once you have candidate messages, fetch thread context for each chatGuid:
GET https://figment.so/api/apple-connect/v1/connections/con_xxxx/messages
?chatGuid=iMessage%3B-%3B%2B15550001234
&limit=50
Authorization: Bearer ACCESS_TOKEN
Each message item includes a guid, text, isFromMe, and chatGuid. Locate your tracked message by its stored guid, then check whether any subsequent item with isFromMe: false appears after it in the returned list. That ordering is your resolution signal — the API does not guarantee a sortable timestamp field on every item.
If historyComplete is false, earlier messages may still be syncing from iCloud. Treat any gap as Awaiting, not Resolved.
Scope and readiness. If your app grant restricts to selected conversations, include chatGuid in every request. Before scanning, confirm the connection is ready: GET /v1/connections/con_xxxx returns connection.services.messages.state; handle non-2xx responses and use the account screen for setup status. A non-ready connection returns an error, not stale data.
When a message reaches Lapsed, your system sends a follow-up. Before making the HTTP call, persist three things in durable storage: the idempotency key, the exact message body, and the target chatGuid. If the process restarts, you need all three to reconcile or safely retry.
Use POST /v1/connections/con_xxxx/messages/send with the Idempotency-Key header:
POST https://figment.so/api/apple-connect/v1/connections/con_xxxx/messages/send
Authorization: Bearer ACCESS_TOKEN
Idempotency-Key: followup-{source_guid}-attempt-1
Content-Type: application/json
{
"chatGuid": "{chatGuid_from_GET_chats}",
"text": "Hey — just checking in on the quote I asked about Friday. Still good to confirm?"
}
Use chatGuid values returned by the chat-list endpoint, not manually assembled strings. The messages:send scope is separate from messages:read, and your send grant must include that specific existing conversation.
After an unknown outcome, do not issue a new attempt number. Poll the original receipt using the same key:
GET https://figment.so/api/apple-connect/v1/connections/con_xxxx/messages/receipt
Authorization: Bearer ACCESS_TOKEN
Idempotency-Key: followup-{source_guid}-attempt-1
The receipt data.state distinguishes submitted, delivered, read, failed, and unknown. A failed receipt is a recorded result — repeating the same key returns the same recorded failure; it does not retry the send. Only assign a new attempt number after you have confirmed the prior attempt failed and you are intentionally making a fresh send. Never resend an unknown action under a new key; poll the same key until the state settles.
Your scan results depend on what the Mac has available. Two settings matter:
Messages in iCloud. When enabled, Apple keeps your entire message history — iMessage, SMS, MMS, and RCS — in sync across devices. Enable it on iPhone under Settings → Apps → Messages (iOS 18 and later; use Settings → Messages on earlier iOS), and on Mac in Messages → Settings → iMessage. When the setting is off, the Mac only has messages received or sent while it was running.
iPhone availability. For SMS, MMS, and RCS threads, the iPhone must be on, online, and signed into the same Apple Account. When Messages in iCloud is enabled, forwarding to the Mac is handled automatically — a separate Text Message Forwarding switch is not always required. (Apple support, July 2026)
Initial sync after a new account connection can leave history incomplete. Build your state machine to tolerate gaps: a missing reply during sync is Awaiting, not Resolved.
The Figment iMessage REST API — search, history, send, and receipt — is available to developers with an approved account and completed connection setup. Live connections cost $29/month per active connection; sandbox access is free and returns synthetic data. The developer app never receives or stores the Apple password.
To configure a connection and request messages:read and messages:send scopes, start at Connect Apple Account. For the full authentication, pagination, and scope reference, see iMessage API for Search, Replies, and Agent Actions.
Related guides