Skip to main content
Migo Docs

Create a Payment Link

A Payment Link is a hosted webview URL that lets a Migo merchant collect a payment from an end customer without writing front-end payment code. Your backend creates the link by calling Migo, and you deliver the resulting URL to the customer through any channel you operate (WhatsApp, SMS, email, in-app message, QR code, etc.).

Your only API call is creating the transaction. The customer then completes the entire payment inside Migo's hosted webview β€” entering the card, choosing installments, passing 3D Secure β€” and Migo reports the outcome back to your backend through a webhook. You never collect card data and never call a "charge" endpoint yourself.

β”Œβ”€ your backend ──────────────┐
β”‚ 1. POST /transactions β”‚ ───────────► Migo
β”‚ (creates the link) β”‚
β”‚ β”‚ ◄─────────── 2. { uid, reference, URL }
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”‚ 3. deliver URL to the customer
β”‚ (WhatsApp Β· SMS Β· email Β· QR Β· in-app)
β–Ό
β”Œβ”€ customer (hosted webview) ─────────────────────────┐
β”‚ 4. opens the URL and pays inside Migo's webview: β”‚
β”‚ enters card Β· picks installments Β· 3D Secure β”‚
β”‚ (no integrator API calls happen here) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚
β”‚ 5. Migo reaches a terminal status
β–Ό
β”Œβ”€ your backend ──────────────┐
β”‚ 6. receives the outcome β”‚ ◄─────────── webhook (Merchant Generic Callback)
β”‚ approved / denied / … β”‚ { uid, reference, status, … }
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Steps 1–3 and 6 are yours; steps 4–5 happen entirely inside Migo. The rest of this page details step 1 (the two ways to create the link) and points you to the webhook for step 6.

Choosing the endpoint​

Migo exposes two endpoints that produce a payment link. They reach the same hosted webview and only differ in how the request is authenticated:

  • POST /transactions (primary, recommended) β€” backend-to-backend with a server-side merchant token. Returns the human-friendly reference.
  • POST /transactions-hook (alternative) β€” browser-friendly, authenticated with a publicKey + privateKey pair in the body. No reference is returned.

If you control a backend, use POST /transactions. If you have no server at all (static SPA), use POST /transactions-hook. For the full decision tree, the side-by-side comparison, and how to combine both, see Choose your flow.

The result of the payment is later delivered to your backend through the Merchant Generic Callback.

Primary: POST /transactions​

This is the recommended endpoint for backend-to-backend integrations.

Endpoint​

POST https://sb-mw.migopayments.com/transactions (Sandbox)
POST https://mw.migopayments.com/transactions (Production)

Content type: application/json. Send the merchant token Migo issued for your application as the value of the Authorization header. The raw token (no scheme prefix) is recommended; a leading Bearer is tolerated and stripped:

Authorization: b6a615d8...0ba65c1 βœ… recommended (raw token)
Authorization: Bearer b6a615d8... βœ… accepted (the "Bearer " prefix is stripped)

For the canonical rules on token format and header conventions, see Authentication.

Request body​

FieldTypeRequiredDescription
amountintegeryesTransaction subtotal in the smallest currency unit allowed by your client configuration. Must fall inside the min/max range configured for your client.
userIdstringyesIdentifier of the end customer in your domain (phone number, email, or your internal id).
channelstringyesCommunication channel through which you intend to deliver the payment link to the end customer (for example wa, app, sms, email, web).
clientstringyesSlug of your client as registered in Migo configuration.
adsarraynoReserved array, send [] if not used.
Optional fields are shared with /transactions-hook

POST /transactions and POST /transactions-hook are consumed by the same transaction builder, so the optional fields documented for /transactions-hook below β€” currency, externalId, customKeys, processorAmount, customerId, subscriptionData, createdBy β€” are equally accepted by POST /transactions. The cURL and SDK examples that send currency/externalId are therefore valid against this endpoint too.

Allowed values for channel, userId, currency​

These three fields are validated against your client configuration. The values you can use depend on what is enabled for your specific client slug:

FieldCommon valuesHow to discover what your client accepts
channelwa (WhatsApp), sms, email, web, appChannels are gated per client. If your call returns ownCode 5000 for channel, the value is not enabled β€” ask your Migo contact to enable it.
userIdE.164 phone (+50224865444), email, or your internal idFormat is free-form: Migo stores it verbatim. Pick the format you can correlate with your own user records and stay consistent. For SMS / WhatsApp delivery the field must be a phone Migo can dispatch to.
currencyISO 4217 (GTQ, USD, MXN, CRC, HNL, DOP, COP, SVC)If omitted, Migo uses the first currency configured for your client. If the value isn't in the configured list it is silently coerced to the default β€” pass it explicitly when you need to be sure.

There is no public "client config" endpoint today, so the enabled channels, currencies, and processors are not self-service discoverable. Coordinate with your Migo contact to confirm what your client slug accepts, and treat an ownCode 5000/5004 response as a signal that a value is not enabled.

cURL example (Sandbox)​

Replace the placeholder token and client slug with the values issued to you for Sandbox.

curl --location 'https://sb-mw.migopayments.com/transactions' \
--header 'Content-Type: application/json' \
--header 'Authorization: <your-sandbox-token>' \
--data '{
"amount": 1,
"userId": "50224865444",
"channel": "wa",
"client": "<your-client-slug>",
"ads": []
}'

Successful response​

{
"uid": "Ypo5z2zTpLqWoe3eVvZMH",
"reference": "CAMPGT020007SB",
"URL": "https://sandbox.migopayments.com/?orderId=Ypo5z2zTpLqWoe3eVvZMH"
}
FieldDescription
uidUnique transaction identifier in Migo. Use it to correlate the transaction with the Merchant Generic Callback and with any support enquiry.
referenceHuman-friendly reference that Migo derives from your client configuration. Surfaced in webhook payloads, dashboards, and refund operations.
URLHosted webview URL you deliver to the end customer. The field name is uppercase (URL, not url), exactly as emitted by the handler β€” keep that casing when you parse the response.
Common pitfall β€” destructuring URL

TypeScript / JavaScript snippets that use const { url } = response will silently get undefined. Always destructure as const { URL: paymentLink } = response or read the field with bracket notation response.URL. The same applies to /transactions-hook below.

Alternative: POST /transactions-hook​

Use this variant in either of the following cases:

  • You don't want to use the bearer-token endpoint. You authenticate with a privateKey + publicKey pair sent in the body, so you don't have to issue and refresh tokens.
  • You need to call from a browser without CORS issues. /transactions-hook is configured to accept cross-origin requests, so a front-end can invoke it directly without a server-side proxy.

The hosted webview produced by this variant is identical to the one produced by /transactions. The only behavior difference visible to the merchant is the response shape (no reference is returned).

Endpoint​

POST https://sb-mw.migopayments.com/transactions-hook (Sandbox)
POST https://mw.migopayments.com/transactions-hook (Production)

Content type: application/json. CORS is enabled and authentication is performed inside the handler against the body credentials, so the request does not require an Authorization header.

Spec path vs. public path

The OpenAPI spec lists this operation under /mw-default/transactions-hook, but the custom domain at *.migopayments.com strips the /mw-default/ prefix. The public path you call is /transactions-hook (no /mw-default/ segment) β€” exactly what the cURL example below uses.

Authentication​

Send these two fields in the JSON body:

FieldDescription
privateKeyPrivate credential of your registered Migo application.
publicKeyPublic credential of your registered Migo application.

Both credentials are issued to you when your application is registered. If they don't match a registered application, the endpoint responds with HTTP 401 and ownCode 2007.

If you call this endpoint from your backend, treat privateKey as a secret and never embed it in URLs. If you call it from a browser, scope each publicKey / privateKey pair to the specific client and amount range you need so that exposure is bounded.

Request body​

FieldTypeRequiredDescription
amountintegeryesTransaction subtotal in the smallest currency unit allowed by your client configuration. Must fall inside the min/max range configured for your client.
userIdstringyesIdentifier of the end customer in your domain.
channelstringyesCommunication channel through which you intend to deliver the payment link to the end customer.
clientstringyesSlug of your client as registered in Migo configuration.
privateKeystringyesPrivate credential of your registered application.
publicKeystringyesPublic credential of your registered application.
currencystringnoCurrency ISO 4217 code (for example GTQ, USD). If omitted, or if not in the list of currencies allowed for your client, Migo uses the first currency configured for the client.
processorAmountobjectnoMap of processor name to amount override. Each value must be <= amount.
customKeysobjectnoArbitrary merchant-defined data preserved verbatim on the transaction. Available to webview templates and to Generic Callback templates.
externalIdstringnoIdentifier you use in your own system to correlate the Migo transaction with your record.
createdBystringnoIdentifier of the operator that created the transaction (used by Migo dashboards).
customerIdstringnoInternal identifier associating the transaction with a customer record. Stored under additionalData.clientCode.
subscriptionDataobjectnoSubscription metadata appended to additionalData.subscriptionInfo.

cURL example (Sandbox)​

curl --location 'https://sb-mw.migopayments.com/transactions-hook' \
--header 'Content-Type: application/json' \
--data-raw '{
"privateKey": "<your-sandbox-private-key>",
"publicKey": "<your-sandbox-public-key>",
"amount": 1,
"userId": "50224865444",
"channel": "app",
"client": "<your-client-slug>",
"createdBy": "operator@example.com"
}'

Successful response​

{
"uid": "7JV3UKvc2XEretWV2Rb57",
"URL": "https://sandbox.migopayments.com/?orderId=7JV3UKvc2XEretWV2Rb57"
}

/transactions-hook returns { uid, URL } β€” there is no reference field. If you need the human-friendly reference, use POST /transactions instead, or read the value from the Merchant Generic Callback payload once the transaction reaches a terminal status.

What the customer does next​

Once you deliver the URL, everything else happens inside Migo's hosted webview β€” there is no further API call for you to make. When the customer opens the link, the webview resolves your client configuration and enabled processors automatically and renders the payment UI. Inside it the customer:

  • enters their card details in the payment form,
  • chooses the number of installments (when the processor supports them),
  • passes the 3D Secure challenge if the processor requires it,
  • and confirms the payment.

Migo tokenizes the card and charges the transaction internally as part of that flow. You never collect card data, never tokenize, and never call a charge endpoint yourself β€” those are webview-internal steps. See Hosted webview for how the checkout behaves inside the webview.

Receiving the result​

Migo notifies your backend once the transaction reaches a terminal status (approved, denied, refunded, reversed). The Payment Link product delivers this through the Merchant Generic Callback β€” an outbound POST from Migo to a URL you register with support, carrying { uid, reference, status, amount, currency, ... }.

MechanismDirectionWhen
Merchant Generic CallbackMigo β†’ your backendThe transaction reaches a terminal status

Use the uid you received in step 1 to correlate the callback with the transaction you created. The callback is not signed with HMAC β€” authenticity is delegated to a static API key/header you register with Migo. See Merchant Generic Callback β†’ Authenticating the request.

Errors​

Both endpoints return a Migo ErrorResponseDto. The most actionable field is ownCode, which maps to a Migo error catalog entry.

Two error code spaces

Payment Link errors use the Middleware ownCode space (low integers like 2002, 2007, 5000, 5004). They are different from the Gateway/CMS error codes (7000–8099) documented in the Error Catalog. The full Payment Link error list is mirrored at the bottom of that page so you can map every code in one place.

HTTPownCodeCauseWhat to do
4095000Required fields missing or invalid bodyCheck that amount, userId, channel, client (and privateKey / publicKey for /transactions-hook) are present with the right types.
4012007Invalid body credentials β€” /transactions-hook onlyVerify the privateKey / publicKey pair against your application registry; make sure you are using Sandbox credentials against Sandbox and Production credentials against Production.
401(none)Invalid token on POST /transactionsThe token is validated by the API Gateway custom authorizer, not the handler. On failure the gateway returns a generic 401 with body { "message": "Unauthorized" } β€” there is no Migo ErrorResponseDto and no ownCode. Verify the merchant token and the Sandbox/Production environment match.
4095004client slug not found in Migo configurationConfirm the slug; create or activate the client config in coordination with Migo.
4092003amount outside the min/max range configured for your merchant account, or any value in processorAmount exceeds amountAdjust the amount, or update the per-processor overrides so each is <= amount.
4002006End customer is excluded for this clientExcluded users cannot transact for this client.
4002004End customer is blocked for this clientBlocked users cannot transact for this client.
4002005End customer is suspended for this clientSuspended users cannot transact while the suspension is active.
4002002Generic creation errorRetry; if the error persists, contact Migo support with the uid (when available) and the request payload.

Next steps​

  1. Deliver the URL to the end customer through your channel of choice.
  2. Implement the Merchant Generic Callback so your backend receives the transaction outcome (approved / denied / refunded / reversed) once the customer completes the webview.
  3. For a copy-paste browser integration, follow the React Payment Link recipe. For a checklist of every value you need in Sandbox, see the Sandbox cheat sheet.
  4. For the rest of the payment lifecycle (refunds, captures, processor-specific behavior, 3DS), explore the Migo Middleware API reference and the additional sections of this portal.