iMessage: what Apple does not publish
A whole-SDK search for a scheduling API, and what turned up. Apple’s own Send Later cannot do it either.
Short answer: no, and not because SentLoop is missing something. Apple's own Messages "Send Later" cannot do it either.
This document records what was checked, how, and what the evidence was. Every claim below is either a symbol read out of the installed SDK or a quote from Apple's current documentation. Where something could not be verified on this machine, it says so instead of guessing.
Verification environment
| Toolchain | Apple Swift 6.3.2, target arm64-apple-macosx26.0 |
| Installed | Command Line Tools only - /Library/Developer/CommandLineTools |
| SDKs present | MacOSX26.sdk, MacOSX26.5.sdk, MacOSX15.sdk, MacOSX15.4.sdk |
| Xcode | Not installed. xcodebuild errors: "requires Xcode, but active developer directory is a command line tools instance" |
| iOS SDK | Absent. No iPhoneOS SDK, no Simulator |
| Physical iPhone | Not available to this environment |
Two consequences, stated up front because they bound everything below:
- The iOS companion app in
apple/SentLoop/has not been compiled. It is written against documented API, and it is marked as unbuilt wherever it is referenced. Nobody should treat it as verified-working code. - Tests A, B and C in the brief were not run. They require an iPhone, a Mac that can be powered off independently, and Xcode. Their answers below come from Apple's documentation, which happens to answer them unambiguously, and they are labelled as documentary rather than experimental.
What was actually inspected
1. The App Intents assistant schemas, read from the SDK
$SDK/System/Library/Frameworks/AppIntents.framework/Versions/A/Modules/
AppIntents.swiftmodule/arm64e-apple-macos.swiftinterface (11,752 lines)
AppIntents.swiftmodule/arm64e-apple-ios-macabi.swiftinterface (iOS ABI)
Every schema domain declared in the installed framework:
assistant books camera files journal mail photos presentation
reader spreadsheet system visualIntelligence whiteboard wordProcessor
Note what is present and what is not: there is a mail domain
(SendDraftIntent, CreateDraftIntent, ReplyMailIntent, ArchiveMailIntent…)
and there is no messages domain. Searching both interfaces for the string
messages returns 0 matches, in the Catalyst/iOS interface as well as the
macOS one.
A whole-SDK sweep for any scheduling symbol:
grep -rlE "scheduleMessage|sendLater|SendLater|scheduledMessage|ScheduledMessage" \
$SDK/System/Library/Frameworks
→ no matches
So on the SDK installed here, no such API exists to call.
2. The messages domain, read from Apple's live documentation
The domain does exist in current documentation - it is simply newer than the
SDK on this machine, which still uses the older @AssistantIntent macro rather
than the @AppIntent(schema:) spelling the docs now show.
Pulled from Apple's documentation JSON (the HTML is JavaScript-rendered and returns nothing useful to a fetch):
Messages - "Make your messaging app's actions available to Apple Intelligence and Siri by adopting schemas for common messaging actions."
Actions in the domain:
draftMessage editSentMessage sendMessage
setMessageReadStatus unsendMessage
There is no scheduling action in the domain. No scheduleMessage, no
sendLater, no cancelScheduledMessage.
3. scheduledDate - the finding that looks like a yes and is not
.messages.sendMessage does carry a date. Apple's own example:
@AppIntent(schema: .messages.sendMessage)
struct SendMessageIntent {
var content: AttributedString?
var destination: MessageDestination
var subject: AttributedString?
var attachments: [IntentFile]
var audioMessage: IntentFile?
var locations: [GeoToolbox.PlaceDescriptor]
var links: [URL]
var scheduledDate: Date? // ← it exists
func perform() async throws -> some ReturnsValue<[MessageEntity]> {
// ← and this body is yours
}
}
This is the single most misreadable fact in the whole area, and the brief was right to flag it.
scheduledDate is an inbound parameter to an intent you implement. Siri
parses "send this tomorrow at nine", fills in scheduledDate, and calls your
perform(). Nothing in Apple's messaging infrastructure has been told anything.
If your app does not itself implement scheduling, the date is simply a Date?
you were handed and ignored.
Apple's integration guide removes any remaining doubt about direction. The entire sample is UnicornChat, a fictional third-party app that messages fictional unicorns:
"when a person says 'Siri, send a message to Bubbles', Apple Intelligence needs to resolve that Bubbles is a contact in the UnicornChat app, that UnicornChat is a messaging app that supports a set of defined messaging actions…"
Adopting .messages.sendMessage makes SentLoop available to Siri. It does
not make Messages available to SentLoop.
4. INSendMessageIntent, read from the SDK header
$SDK/System/Library/Frameworks/Intents.framework/.../INSendMessageIntent.h
Every property on the intent:
recipients outgoingMessageType content speakableGroupName
conversationIdentifier serviceName sender attachments
No date. No scheduling parameter of any kind. The brief noted Apple's documentation says this intent does not inherently require an unlocked device; that is true and also irrelevant, because there is nothing here to schedule.
Direction is settled by the handler protocol, quoting the header:
@protocol INSendMessageIntentHandling <NSObject>
@required
/*! @abstract Handling method - Execute the task represented by the
INSendMessageIntent that's passed in
@discussion Called to actually execute the intent.
The app must return a response for this intent. */
"The app must return a response" - the system calls your app. This is the same shape as the App Intents schema, one generation older.
5. Apple's own Send Later - the control experiment, answered by documentation
Messages has had a native Send Later since iOS 18: + → Send Later, up to
14 days out. The decisive property of it is the one that answers this brief:
"Scheduled messages require your iPhone to be powered on with an active internet connection at the scheduled time. If your device is off or disconnected, the message won't send automatically when you reconnect."
Apple's own first-party scheduling is device-dependent. The schedule lives on the iPhone, not on Apple's servers. There is no server-side queue to hand a message to, which is why no public API exposes one - the thing the brief is asking for does not exist for Apple either.
That single fact makes the acceptance test unachievable by anyone, including Apple, and it means no private API would help. There is no back door to a capability that is not implemented anywhere.
The ten questions, answered
1. Can SentLoop schedule a native iMessage through a public API?
No. The messages App Intent domain exposes your app to Siri. There is no
public API that inserts a message into Apple's Messages scheduling.
2. Can the operation be performed while the iPhone is locked?
An App Intent can be invoked while locked, and INSendMessageIntent does not
inherently require an unlocked device. But the operation in question does not
exist, so the property has nothing to attach to. For SentLoop's own device-
assisted path, a Shortcuts automation does run on a locked phone.
3. Can SentLoop initiate it while running? It can run its own intent while running. It cannot cause Apple Messages to schedule anything, running or otherwise.
4. Does the resulting scheduled message persist if SentLoop is terminated? There is no Apple-held schedule to persist. A schedule SentLoop keeps lives on SentLoop's server and survives the app being killed - but delivery still needs a device at the appointed moment.
5. Can the iPhone subsequently be offline? No. Apple's own Send Later fails this, per the quote above.
6. Can the Mac be powered off? Yes, irrelevantly - the Mac is not required by any iPhone-based path. It matters only for SentLoop's macOS agent route, which does require it awake.
7. Does Apple perform the eventual delivery? Apple's servers relay an iMessage once a device submits it. Apple does not hold a pending scheduled message on behalf of a third-party app, and does not hold one for its own Send Later either.
8. What exact public APIs are responsible?
AppIntents-@AppIntent(schema: .messages.sendMessage)and the rest of themessagesdomain, for exposing SentLoop to Siri and Apple Intelligence.Intents-INSendMessageIntent/INSendMessageIntentHandling, the older equivalent, kept for compatibility and for share-sheet donation.Shortcuts- a time-of-day Personal Automation can run a shortcut that sends, which is the only unattended-ish path and still needs the phone awake.
9. What limitations remain? Delivery requires a powered-on, online Apple device belonging to the user at the scheduled moment. Everything else is arrangeable.
10. What functionality is impossible without private APIs? Server-side, device-independent iMessage delivery is impossible with private APIs too. Private APIs could impersonate a Messages client on a device you control; they cannot create server-held scheduling that Apple has not built. Services advertising an "iMessage API" run racks of real Apple hardware - that is someone else's device staying awake, not an API.
Final acceptance test
User schedules an iMessage at 11:00 PM for 8:00 AM. At 11:01 PM SentLoop is closed, iPhone locked, Mac shut down. At 8:00 AM the recipient receives it.
TRUE ZERO-DEVICE AVAILABILITY: NOT SUPPORTED
PUBLIC APPLE API: NOT AVAILABLE
PRIVATE API REQUIRED: NO - and it would not help
FALLBACK: Device-assisted scheduling (below)
Locked and app-closed are both fine. Powered off is not. The test as written fails on one clause, and it fails identically if you replace SentLoop with Apple's own Messages app.
What SentLoop builds instead
Device-assisted scheduling, labelled as exactly that in the product. The schedule is authoritative on the server; a device the user already owns performs the send at the appointed time.
SentLoop server (holds the schedule, survives everything)
↓ device asks: is anything due?
iPhone Shortcut or macOS agent
↓ Apple's own Send Message action
Apple Messages infrastructure
↓
Recipient
Two device paths, both first-party surfaces, neither scripting the UI:
- iPhone,
agent/iphone.md- a Shortcuts Personal Automation. Runs on a locked phone with SentLoop closed. Needs the phone on. - Mac,
agent/README.md-shortcuts run, a documented CLI. Needs the Mac awake.
And the honest part, which is already enforced in code rather than promised in
prose: when no device is available, SentLoop does not sit on the message. The
resolver moves it to a channel that sends from the server - email, Telegram,
Discord, Slack, Mastodon, Bluesky - so the message still arrives. That behaviour
is asserted by npm run verify:devices-off, which forces the no-Mac condition
and proves the iMessage leg is skipped rather than attempted.
What SentLoop will therefore display, and will not exceed:
Scheduled through Apple-supported device automation.
Your device may need to remain available.
The sentence "Your iPhone and Mac do not need to remain available" is never shown for iMessage, because no test supports it and none can.
What is not proven here
Being explicit, because the brief asks for the boundary rather than a demo:
- The iOS companion app is uncompiled. No Xcode on this machine.
- Tests A, B and C were not executed. No iPhone, no independently power-cyclable Mac.
- The
messagesschema is not in the SDK installed here; it was read from Apple's current documentation. On a machine with current Xcode the macro spelling should be confirmed before shipping. - Whether Apple's native Send Later is exposed as a Shortcuts action was not verified on-device. It would not change the conclusion, since Send Later is device-dependent regardless.
Anyone with an iPhone and Xcode can close these gaps, and the capability screen in the companion app is built to record the answers rather than assume them.