Private payment phone search
Use POST /v1/payments/search/phone with a server-held API key granting payments:read. The body is { "phone": "+243810000001", "filters": {}, "cursor": null }. Only canonical full-number exact matches are supported. Keep the phone out of URLs, logs, analytics and persistent browser storage. This passive POST does not initiate payments or require an idempotency key. Requests still consume merchant quota.
Filters are conjunctive: reference, minor-unit amount as a decimal string, currency, state, method, branch, from/to instants and supported display timezone. Results contain at most 25 rows, integer minor-unit amounts and an optional next_cursor; send it in the next body with the same phone and filters. CDF amounts are divisible by 100. Each page is a separate snapshot. A cursor from another tenant, phone or filter cohort is rejected.
Coverage is always INDEXED_PHONE_ADMISSIONS, with historical_complete: false. Only newly indexed synthetic wallet admissions are represented. Old payments, admissions while indexing was disabled and bank payments without phone capture are not reconstructed. An empty result does not prove historical absence or phone ownership. If retained key material is unavailable, the service returns 503 instead of a partial report.
For matching downloads use POST /v1/payments/search/phone/export with { "phone": "+243810000001", "filters": { "from": "2026-01-01T00:00:00Z", "to": "2026-01-02T00:00:00Z" }, "format": "csv" }. Choose csv or xlsx. The inclusive from and exclusive to must span at most 366 days. No cursor is allowed. More than 5,000 matching rows returns 413; narrow the interval or filters. CSV protects against spreadsheet formulas, and XLSX uses explicit text cells. Never substitute the unfiltered GET export for a phone search.
Team equivalents use /portal/merchants/{merchant}/payments/search/phone and /export, a server-held password-plus-OTP identity and the current X-Futa-Membership-Version. Current role, version, revocation and quota are checked on every request. Keep all credentials out of browser JavaScript. All private responses are no-store. Clear transient phone/results after backgrounding, logout or access changes; the portal consumer is a separate delivery gate.
Export responses carry X-Futa-Phone-Coverage: INDEXED_PHONE_ADMISSIONS and X-Futa-Historical-Complete: false; retain this disclosure when presenting downloads. The service validates bounded upstream responses, and nothing retries a search for you. Futa’s generated TypeScript client, kept for its own use and not published, names these operations searchPaymentsByPhone, searchTeamPaymentsByPhone, exportPaymentsByPhoneRaw and exportTeamPaymentsByPhoneRaw (MerchantPaymentPhoneApi): its raw exports expose those headers before await response.value() gives the file, its date filters take Date and its amount filters strings, and its types are not runtime validators. Codes include 400 malformed input, 401/403 denied authority, 404 foreign search cursor, 409 stale team version, 413 oversized export, 429 quota and 503 unavailable lookup or invalid upstream data.