Frequently Asked Questions
Last Updated: 2026-05-05
Common questions partner developers ask while integrating with MallPlus Open Platform. If your question isn't here, see the topical guides under Getting Started or open a support ticket.
Authentication & credentials
Where do I get my client_id and client_secret?
Sign in to the partner console, create an app on /apps/create, and copy the credentials shown immediately after creation. The client_secret is shown ONCE — store it in a secret manager. If you lose it, rotate via /apps/<id> → Rotate Secret.
My request returns 401 Invalid signature. What do I check?
In order: (1) Is the timestamp in seconds (not milliseconds)? (2) Is the timestamp within ±60s of server time? (3) Is the base string exactly `${timestamp}:${clientId}:${METHOD}:${path}:${sha256(body)}` for HMAC-v2? (4) Is the signature lowercase hex? (5) Are you signing with the secret from the same environment as the clientId (sandbox vs live)?
How do I rotate a leaked secret?
POST /open/v1/credentials/rotate-secret (sandbox) or /rotate-live-secret (production, app status = live). The response includes the new secret ONCE. The old secret is invalidated immediately — schedule the rollout to coincide with cache flush in your app.
How long does an access token live?
Access token: 4 hours. Refresh token: 30 days. Use POST /open/v1/auth/token/refresh to mint a new pair. The refresh token is rotated each call.
Can I use HMAC v1 (the old base string without method/body)?
Yes — v1 is accepted with a deprecation warning. Plan to migrate to v2 by your next minor release; v1 will be removed in a future version.
Webhooks
How do I verify a webhook came from MallPlus?
Check the X-MallPlus-Signature header: it is HMAC-SHA256(timestamp:event:body, secret). Use the SAME client_secret you sign requests with (live secret for live apps, sandbox secret otherwise). Reject requests where the timestamp is more than 5 minutes old.
What is the retry policy?
A delivery is attempted inline once, then queued for retry on any non-2xx or network error. Backoff is exponential (4s × 4^attempt) up to 3 attempts. After the last attempt the row goes to status="dead_letter" and stays for forensic inspection — it does not auto-retry.
What happens if my callback URL is briefly down?
It is retried per the schedule above (~1 hour total wall time across 3 attempts). If it stays down past that, the event is dead-lettered. We do NOT replay dead-lettered events automatically — open a support ticket if you need a manual replay.
My subscription was created but I receive no events. Why?
Common causes: (1) the subscription is inactive (set isActive=true), (2) the partner app has been soft-deleted, (3) the callback URL fails the SSRF guard (private IPs blocked), (4) every delivery is being dead-lettered — check /tools/push-log.
Can the same (event, callback) be subscribed twice?
No. The `(partner_app_id, event_type, callback_url)` triplet has a unique partial index. The second create returns 409 CONFLICT.
Rate limits
What are the limits?
Default: 100 requests/minute on read endpoints, 30 requests/minute on write endpoints, scoped per app. The limiter applies per-IP first (cheap pre-auth shield) then per-app (verified after partner-auth). Headers X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset are returned on every response.
I got 429 — what now?
Honour the Retry-After header (or back off using X-RateLimit-Reset). Implement exponential backoff with jitter on retries; do not hammer the endpoint or your IP bucket can be temporarily blocked too.
Can I get higher limits?
Yes for production-grade integrations. Open a support ticket with: app name, expected peak QPS, and the endpoints you need lifted.
Sandbox vs production
How do I get production credentials?
Submit a Go Live application from /apps/<id>/go-live. An admin reviews the production URL, integration description, and any KYC documents. On approval, you receive `clientIdLive` and `clientSecretLive` (shown once). Status moves from `pending_go_live` → `live`.
Can I use the same app for both?
Yes. One app holds both sandbox (`mp_*`) and live (`mp_live_*`) credentials. Use whichever is appropriate — the API auto-detects which environment from the credential prefix.
When is sandbox data reset?
Sandbox data is NOT reset on a schedule — it accumulates as you test. If you want a clean slate, use the seed endpoints in dev/staging, or request a manual reset via support ticket.
Which secret signs webhooks in production?
The live secret if your app status is `live`. Sandbox apps sign with the sandbox secret. The dispatcher always uses the secret matching the app environment — your verifier should pick whichever was issued to you most recently for the running env.
Errors & debugging
What is the response shape on error?
Always `{ success: false, error: { code, message } }`. The `code` field is a stable enum from `ERROR_CODES` (e.g. UNAUTHORIZED, VALIDATION_ERROR, NOT_FOUND, RATE_LIMITED). Do not parse `message` — it is human-readable and may change.
I see TIMESTAMP_EXPIRED but my clock looks correct.
The window is ±60s. If your container drifts, sync NTP. If you are running in a serverless cold-start, use the request reception time, not your function start time. The server compares against UTC seconds — make sure you are not using milliseconds.
My authenticated request gets a 404 instead of the expected 403.
That is by design — cross-tenant access (e.g. another seller's order) returns 404, not 403, to avoid leaking the existence of resources you don't own.
How do I get the request ID for support?
Every response includes an `X-Request-Id` header. Include it in your support ticket so we can grep the access log directly.
Support
Where do I open a ticket?
Partner console → /support/raise-ticket. Categories: API issue, Authentication, Webhooks, Sandbox & Testing, Account & App Management, Other. Attach the X-Request-Id from any failing call so we can pull the access log.
How fast do you respond?
High priority: same business day. Medium: 1–2 business days. Low: best-effort. We bump priority automatically on production-blocking incidents reported with a request id.
Where is the changelog?
/docs/changelog. Subscribe via the partner-portal email preferences in /settings/account. Breaking changes are flagged at least 30 days in advance.
Still stuck?
Open a support ticket and include the X-Request-Id header from any failing call. We respond within 1–2 business days.