Payment Links+
このページで扱うトピック
Payment Links+ API
Payment Links+ is the solution for creating secure payment links with your branding. With Payment Links+, you can effortlessly set up and share payment links, regardless of the sales channel or payment method. To learn more, check out Payment Links+.
This document explains the Payment Links+ API functions.
🔓 Authorization
The Payment Links+ API requires an API key to authenticate calls. Retrieve this key from Settings > API Keys on the Payment Links+ dashboard, and pass it in the Authorization header.
curl --location 'https://linksplus-api.omise.co/external/links' \
--header 'Authorization: {{key}}'
⚠️ Roll your key with care: once a key is rolled from the API Keys screen, the old value is never shown again. If you lose track of the current key, generate a new one by rolling it again — there's no way to retrieve a rolled key afterward.
🧾 Objects
Every endpoint on this page returns one of the objects described here. Rather than repeating the full shape under each endpoint, each one is defined once and referenced by name throughout.
The Link object
| Field | Type | Description |
|---|---|---|
id |
integer | The link's unique ID. |
name |
string | The link's display name. Up to 45 characters. |
currency |
string | The payment currency. |
amount |
integer | The charge amount, in the smallest unit of currency. Minimum 100. |
multiple_usage |
boolean | Whether the link can be used more than once. |
additional |
string, nullable | A free-text note about the link, up to 255 characters. Hidden by default on the payment form. |
expired_at |
string, ISO 8601, nullable | When the link expires. Returns null if no expiry is set. |
features |
object | Key-value pairs matching the product detail fields configured on the link's template. |
transaction_id |
string | The ID portion of url. |
url |
string | The URL to share with the customer to complete payment. |
state |
string | The link's lifecycle stage: "active", "expired", or "used". |
template_id |
integer | The template the link was created from. |
kind |
string, nullable | null for a standard link, "quick_link" for a link created without a manually selected template. |
created_at |
string, ISO 8601 | When the link was created. |
updated_at |
string, ISO 8601 | When the link was last updated. |
{
"id": 12840,
"name": "Adidas School Wear",
"state": "active",
"amount": 4000,
"currency": "THB",
"multiple_usage": true,
"additional": "Order #12345 - priority shipping",
"expired_at": "2026-12-31T23:59:59Z",
"features": {
"Type": "School Bags",
"Size": "Medium"
},
"transaction_id": "lNeJd4VbUI",
"url": "https://linksplus.omise.co/lNeJd4VbUI",
"template_id": 3672,
"kind": null,
"created_at": "2024-04-20T12:23:39.321Z",
"updated_at": "2024-04-20T12:23:39.321Z"
}
The Template object
| Field | Type | Description |
|---|---|---|
id |
integer | The template's unique ID. |
name |
string | The template's display name. |
features |
array of objects | The product detail fields configured on the template. Each entry has display_name, value (always null here, reflecting the field definition rather than a filled-in value), and type. |
capabilities |
array of Capability objects | The payment methods, currencies, and card brands available on links created from this template. |
zero_interest_installment |
boolean | Whether zero-interest installments are enabled. |
created_at |
string, ISO 8601 | When the template was created. |
updated_at |
string, ISO 8601 | When the template was last updated. |
ℹ️ Note: a template's features is an array of field *definitions* (label, type, and an empty value). A link's features is a flat object of the actual values entered for that link. Don't expect the same shape from both.
{
"id": 3672,
"name": "Adidas Bags",
"features": [
{ "display_name": "Type", "value": null, "type": "string" },
{ "display_name": "Size", "value": null, "type": "string" }
],
"capabilities": [
"... see The Capability object for the \"card\" entry ...",
"... a second entry with \"payment_method\": \"promptpay\" ..."
],
"zero_interest_installment": false,
"created_at": "2024-04-19T10:16:49.689Z",
"updated_at": "2024-04-19T10:16:49.689Z"
}
The Capability object
Each entry in a Template object's capabilities array describes one payment method available on it. A template supporting multiple payment methods has one Capability object per method — the example following shows a single card entry, followed by what the full array looks like when a template also accepts PromptPay.
| Field | Type | Description |
|---|---|---|
payment_method |
string | e.g. "card", "internet_banking_bbl", "promptpay", "truemoney". |
currencies |
array of strings | Currencies supported for this payment method. |
card_brands |
array of strings | e.g. "JCB", "Visa", "MasterCard". Present even for non-card methods, though it doesn't apply to them. |
installment_terms |
array | Available installment terms, if any. |
banks |
array | Available banks, if any. |
manual |
boolean | Card capability only. |
saveCard |
boolean | Card capability only. |
{
"payment_method": "card",
"currencies": ["THB", "JPY", "USD", "EUR", "GBP", "SGD", "AUD", "CHF", "CNY", "DKK", "HKD", "MYR"],
"card_brands": ["JCB", "Visa", "MasterCard"],
"installment_terms": [],
"banks": [],
"manual": false,
"saveCard": false
}
A capabilities array with more than one payment method looks like this — note that manual and saveCard only appear on the card entry, matching the preceding table:
[
{
"payment_method": "card",
"currencies": ["THB", "JPY", "USD", "EUR", "GBP", "SGD", "AUD", "CHF", "CNY", "DKK", "HKD", "MYR"],
"card_brands": ["JCB", "Visa", "MasterCard"],
"installment_terms": [],
"banks": [],
"manual": false,
"saveCard": false
},
{
"payment_method": "promptpay",
"currencies": ["THB"],
"card_brands": ["JCB", "Visa", "MasterCard"],
"installment_terms": [],
"banks": []
}
]
🚫 Errors
Every error response shares the same shape:
{
"object": "error",
"code": "bad_request",
"message": "human-readable description of what went wrong"
}
The 401 authentication error additionally includes a location field linking to the API errors reference:
{
"object": "error",
"location": "https://www.omise.co/api-errors#authentication-failure",
"code": "authentication_failure",
"message": "authentication failed"
}
| HTTP Status | code |
Meaning |
|---|---|---|
400 |
bad_request |
The request is malformed, or a parameter failed validation. The message field describes the specific problem — see each endpoint's own section for examples unique to it. |
401 |
authentication_failure |
The API key is missing or invalid. |
403 |
forbidden |
Your API key doesn't have permission for this action, or doesn't have access to the company that owns the resource. |
404 |
not_found |
The resource in the URL path (a link, template, or team ID) doesn't exist. |
Only unique error cases are called out again under individual endpoints. Every endpoint can also return 400, 401, and 403, whether or not they're repeated there.
🛠️ Endpoints
Retrieve a template
GET /external/templates/:template_id
Returns the Template object for the specified ID. Find a template's ID on the Payment Links+ dashboard, under Create Template — the 4-digit number shown under each template thumbnail.
Path parameter: template_id (integer, required)
curl 'https://linksplus-api.omise.co/external/templates/3655' \
-X GET \
-h 'Authorization: {{key}}'
Returns: a Template object on success (200):
{
"id": 3655,
"name": "Nike Shoes",
"features": [],
"capabilities": ["... see The Capability object ..."],
"zero_interest_installment": false,
"created_at": "2024-04-17T08:35:55.390Z",
"updated_at": "2024-04-17T08:35:55.390Z"
}
Unique errors: 404 if the template ID doesn't exist ("message": "template not found").
Retrieve all templates for a team
GET /external/:team_id/templates
Returns the templates belonging to the specified team. Find a team's ID on the dashboard, under Settings > Teams.
Path parameter: team_id (integer, required)
curl 'https://linksplus-api.omise.co/external/3388/templates' \
-X GET \
-h 'Authorization: {{key}}'
Returns: {"templates": [Template, ...], "total": N}:
{
"templates": [
{
"id": 3672,
"name": "Adidas Bags",
"features": [
{ "display_name": "Type", "value": null, "type": "string" },
{ "display_name": "Size", "value": null, "type": "string" }
],
"capabilities": [
"... see The Capability object for the \"card\" entry ...",
"... a second entry with \"payment_method\": \"promptpay\" ..."
],
"zero_interest_installment": false,
"created_at": "2024-04-19T10:16:49.689Z",
"updated_at": "2024-04-19T10:16:49.689Z"
},
{
"id": 3655,
"name": "Nike Shoes",
"features": [],
"capabilities": ["... see The Capability object ..."],
"zero_interest_installment": false,
"created_at": "2024-04-17T08:35:55.390Z",
"updated_at": "2024-04-17T08:35:55.390Z"
}
],
"total": 2
}
Unique errors: 404 if the team ID doesn't exist ("message": "team not found").
Retrieve a link
GET /external/links/:link_id
Returns the Link object for the specified ID. Click Overview, then a payment link, to find its ID in the dashboard URL.
Path parameter: link_id (integer, required)
curl 'https://linksplus-api.omise.co/external/links/12838' \
-X GET \
-h 'Authorization: {{key}}'
Returns: a Link object on success (200):
{
"id": 12838,
"name": "Adidas Bags",
"state": "active",
"amount": 200000,
"currency": "THB",
"multiple_usage": true,
"additional": null,
"expired_at": null,
"features": {
"Type": "School Bags",
"Size": "Medium"
},
"transaction_id": "q16j2t7uF2",
"url": "https://linksplus.omise.co/q16j2t7uF2",
"template_id": 3672,
"kind": null,
"created_at": "2024-04-19T10:58:37.429Z",
"updated_at": "2024-04-19T10:58:37.429Z"
}
Unique errors: 404 if the link ID doesn't exist ("message": "link not found").
Retrieve all links for a team
GET /external/:team_id/links
Returns the links belonging to the specified team. Find a team's ID on the dashboard, under Settings > Teams.
Path parameter: team_id (integer, required)
curl 'https://linksplus-api.omise.co/external/3388/links' \
-X GET \
-h 'Authorization: {{key}}'
Returns: {"links": [Link, ...], "total": N}:
{
"links": [
{
"id": 12838,
"name": "Adidas Bags",
"state": "active",
"amount": 200000,
"currency": "THB",
"multiple_usage": true,
"additional": null,
"expired_at": null,
"features": {
"Type": "School Bags",
"Size": "Medium"
},
"transaction_id": "q16j2t7uF2",
"url": "https://linksplus.omise.co/q16j2t7uF2",
"template_id": 3672,
"kind": null,
"created_at": "2024-04-19T10:58:37.429Z",
"updated_at": "2024-04-19T10:58:37.429Z"
}
],
"total": 1
}
Unique errors: 404 if the team ID doesn't exist ("message": "team not found").
Create a payment link
POST /external/links
Creates a link from the specified template. If the template has product detail fields configured, you must pass the value of each as a features key/value pair.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
template_id |
integer | Yes | The template to use for the link. |
team_id |
integer | Yes | The team that will own the link. Find a team's ID on the dashboard, under Settings > Teams. |
name |
string | Yes | The link's display name. Up to 45 characters. |
currency |
string | Yes | The payment currency. |
amount |
integer | Yes | The charge amount, in the smallest unit of currency. Minimum 100. |
multiple_usage |
boolean | Yes | Whether the link can be used more than once. |
additional |
string, nullable | No | A free-text note about the link, up to 255 characters. Hidden by default on the payment form. |
expired_at |
string, nullable | No | An ISO 8601 date-time with any timezone offset (e.g. 2026-12-31T23:59:59Z), stored and returned in UTC. Must be a future date. Same format as on Update a link. |
features |
object | Conditional | Required only if the template has product detail fields configured. Each key must exactly match a configured field name (e.g. Date, Order_ID) — a key that doesn't match returns an error. |
custom_webhook |
string | No | A custom HTTPS webhook URL for this link — see Webhook configuration, following. |
returnUrl |
string | No | The URL to redirect users to after payment. Priority order: (1) this value, (2) the template's configured return URL, (3) the account's return URL under Settings > Return URL. If none are set, no redirect button appears on the success page. |
curl 'https://linksplus-api.omise.co/external/links' \
-X POST \
-h 'Authorization: {{key}}' \
-d "template_id=3672" \
-d "team_id=3388" \
-d "name=Adidas School Wear" \
-d "currency=THB" \
-d "amount=4000" \
-d "features[Type]=School Bags" \
-d "features[Size]=Medium" \
-d "multiple_usage=true" \
-d "additional=Order #12345 - priority shipping" \
-d "expired_at=2026-12-31T23:59:59Z" \
-d "custom_webhook=https://merchant.com/payment-webhook" \
-d "returnUrl=https://demostore.com/payment_status"
Both form-encoded (as shown) and raw JSON request bodies work — Postman's Body → raw → JSON mode sends the same fields as a nested JSON object instead of -d flags.
Returns: a Link object on success (201):
{
"id": 12840,
"name": "Adidas School Wear",
"state": "active",
"amount": 4000,
"currency": "THB",
"multiple_usage": true,
"additional": "Order #12345 - priority shipping",
"expired_at": "2026-12-31T23:59:59Z",
"features": {
"Type": "School Bags",
"Size": "Medium"
},
"transaction_id": "lNeJd4VbUI",
"url": "https://linksplus.omise.co/lNeJd4VbUI",
"template_id": 3672,
"kind": null,
"created_at": "2024-04-20T12:23:39.321Z",
"updated_at": "2024-04-20T12:23:39.321Z"
}
Unique errors: 400 if the template has product detail fields but a required features key is missing ("message": "invalid features. \"Date\" is required").
Webhook configuration
Payment Links+ supports up to three webhook endpoints per payment:
- Default System Webhook — configured automatically by Payment Links+.
- Dashboard Webhook — configured in your Merchant Dashboard and fetched automatically.
- Custom API Webhook — set per link via
custom_webhook(v3.10.0+).
⚠️ Requirement: only HTTPS URLs are accepted for custom_webhook. A custom webhook failure never blocks payment processing — the other configured webhooks still fire normally, and all configured webhooks receive the same event notifications.
Update a link
PUT /external/links/:link_id
Updates an existing link. Every body field is optional — send only what you want to change.
Path parameter: link_id (integer, required)
Parameters
| Name | Type | Description |
|---|---|---|
template_id |
integer | A different template for the link. |
name |
string | Up to 45 characters. |
currency |
string | One of THB, USD, EUR, GBP, JPY, SGD, MYR. Narrower than the currencies available when creating a link — AUD, CHF, CNY, DKK, and HKD can't be set through this endpoint. |
amount |
integer | Minimum 100. |
multiple_usage |
boolean | Whether the link can be used more than once. |
features |
object | Link-specific feature values. |
additional |
string | Up to 255 characters. Hidden by default on the payment form. |
expired_at |
string, nullable | An ISO 8601 date-time with any timezone offset (e.g. 2026-12-31T23:59:59Z), stored and returned in UTC. Must be a future date. Pass null to clear an existing expiry. |
returnUrl |
string | The URL to redirect users to after payment. |
ℹ️ Note: expired_at uses the same ISO 8601 date-time format on Create a payment link as it does here.
ℹ️ Note: team_id can't be changed through this endpoint — a link can't be moved to a different team after creation.
curl 'https://linksplus-api.omise.co/external/links/789' \
-X PUT \
-h 'Authorization: {{key}}' \
-H 'Content-Type: application/json' \
-d '{
"name": "Updated Payment for Order #12345",
"amount": 15000,
"expired_at": "2026-12-31T23:59:59Z"
}'
Returns: the updated Link object on success (200):
{
"id": 789,
"name": "Updated Payment for Order #12345",
"state": "active",
"amount": 15000,
"currency": "THB",
"multiple_usage": false,
"additional": null,
"expired_at": "2026-12-31T23:59:59Z",
"features": {},
"transaction_id": "wz4qSdsqCG",
"url": "https://linksplus.omise.co/wz4qSdsqCG",
"template_id": 2615,
"kind": null,
"created_at": "2024-09-30T06:21:37.251Z",
"updated_at": "2024-10-01T09:26:21.416Z"
}
Unique errors: 404 if the link ID doesn't exist.
💳 Tracking payments after creating a link
Once you've created a link, send its url to the customer to complete the payment.
ℹ️ Dashboard timing differs between Payment Links+ and Omise: a created link appears immediately in the Payment Links+ dashboard. In the Omise Payments dashboard, the corresponding charge doesn't appear until the customer actually attempts the transaction — a completed payment is marked successful, a customer who drops off during the OTP step leaves it pending, and a customer who cancels during OTP leaves it marked failed.
Finding a charge on the Omise dashboard
After a customer attempts or completes a transaction, find the matching charge using either the link's id or its features:
- Go to Charges on the Omise Payments dashboard.
- Select Advanced Search.
- Choose Metadata as the filter.
- Enter the
idorfeaturesvalue from the API response.
ℹ️ Webhook field mapping: webhook payloads use link_id rather than id — link_id in a webhook corresponds to the id field returned by the API.
❓ FAQ
Q: Does the create-link endpoint accept JSON, or only form-encoded data?
A: Both. Send parameters as form-encoded -d fields in curl, or as a raw JSON body — Postman's Body → raw → JSON mode works the same way.
Q: What format does expired_at expect?
A: The same on both endpoints — an ISO 8601 date-time string with any timezone offset (e.g. 2026-12-31T23:59:59Z), stored and returned in UTC, and must be a future date.
Q: Can I change which team a link belongs to?
A: No. team_id is required when creating a link but isn't accepted when updating one — a link can't move between teams after creation.
Q: I passed a features value and still got a validation error. Why?
A: The keys must exactly match the product detail field names configured on the template, not any name you choose. Check the template's configuration in Create Template on the dashboard for the exact field names.
Q: What values can a link's state have?
A: active, expired, or used — see The Link object.
Q: I don't see a charge on the Omise dashboard right after creating a link. Is something wrong? A: No — a charge only appears there once the customer attempts the transaction. It appears immediately on the Payment Links+ dashboard.
Q: How do I set a custom webhook without going through the dashboard?
A: Pass a HTTPS URL in custom_webhook when creating the link — see Webhook configuration.