TL;DR

Short answer: treat the SMS OTP API as transport while your application owns a small, atomic challenge state machine for issue, verify, resend, cancel, and expiry. The provider should transport messages and report outcomes; it should not become the authority on whether a login attempt is still valid. For a US/EU app builder, the decisive tests are idempotency, country-aware policy controls, observable delivery states, and a contract that makes late events harmless.

There isn't one universally best API. A managed verification API can reduce the amount of messaging machinery a team operates, while a generic SMS API gives the application tighter control over challenge state. The right boundary depends on which layer must own cancellation, abuse policy, and audit evidence.

Decision record: invariants and failure boundaries

The decision is to keep OTP authorization state in the login service and place the SMS API behind a narrow transport interface. A challenge has an opaque identifier, a salted code digest, an expiry, an attempt counter, a send generation, and one terminal status: verified, canceled, expired, or locked. The SMS provider gets the rendered message and a correlation identifier. It never gets authority to reopen a terminal challenge.