Transaction Metadata on iOS
Attach your own JSON context to a sale, preauthorization, or refund with the optional metadata parameter. It is available in iOS SDK 1.0.21 and later in UAT and Production. Install or update the SDK before using these examples.
What is stored
Metadata is an optional JSON object containing your application's context, such as an order ID, invoice reference, cart ID, or refund reason. Use it to reconcile Koard events with your own records and explain why a payment operation occurred.
It belongs to the individual transaction event that the request creates. It is separate from eventId (idempotency), transactionId (the payment lifecycle), and payment settings such as amount, tax, tip, or surcharge. Putting those settings in metadata does not configure the payment.
- Use string keys and JSON values: strings, numbers, booleans, nulls, arrays, or nested objects. Convert dates and custom application objects to JSON-compatible values first.
- Omitting metadata stores no custom metadata for that event. An empty object
{}is valid and is distinct from no value. - A refund can carry different metadata from the original sale. Later events do not automatically copy or merge the previous event's metadata. Pass the order reference again when you want it on a refund.
- Metadata is stored by Koard for retrieval. It is not a processor configuration field or a guarantee of text appearing on a receipt.
- Include business references only; do not include card numbers, CVVs, PINs, API keys, session tokens, or other secrets.
Send metadata with a sale or preauth
Use a Swift dictionary of type [String: Any] whose values can be encoded as JSON. The example assumes the SDK is initialized, the merchant is signed in, a location is selected, and the reader is ready.
import KoardSDK
@MainActor
func saleWithMetadata(eventId: String) async throws -> TransactionResponse {
let metadata: [String: Any] = [
"order_id": "order-123",
"channel": "mobile",
"fulfillment": ["pickup": true]
]
return try await KoardMerchantSDK.shared.sale(
amount: 1250, // $12.50 in minor currency units
breakdown: nil,
currency: CurrencyCode(currencyCode: "USD", displayName: "US Dollar"),
eventId: eventId,
metadata: metadata
)
}
Use a UUID4 for eventId and follow the idempotency guidance for retries. Handle the returned payment status as in Running Payments and save response.transactionId; a returned response alone does not mean the payment was approved.
For an authorization without capture, replace sale(...) with preauth(...); the amount, breakdown, currency, eventId, and metadata arguments above are the same. Run the sale or the preauth according to your payment flow.
Send metadata with a refund
import KoardSDK
@MainActor
func refundWithMetadata(
transactionId: String,
eventId: String
) async throws -> TransactionResponse {
try await KoardMerchantSDK.shared.refund(
transactionId: transactionId,
amount: 500, // $5.00; nil requests a full refund
eventId: eventId,
metadata: [
"order_id": "order-123",
"return_id": "return-456",
"reason": "customer_return"
]
)
}
The default refund does not request another tap. To use a card-present refund, supply withTap: true before metadata; the same dictionary is included with that refund request.
How it reaches Koard
The SDK encodes your dictionary into the payment request's metadata JSON field alongside the encrypted reader data. Sale/preauth use the v5 payment request; refunds carry metadata in their refund request. You do not need to construct the SDK's AnyCodable wrapper or encode the dictionary yourself.
Omit the parameter or pass nil when no custom data is needed. Unsupported values such as custom class instances cause JSON encoding to fail; use strings or JSON objects for those values. Metadata does not change the Apple reader UI, requested amount, or cardholder verification.
Retrieve and view metadata
The current SDK transaction response models do not expose a typed metadata property. Retrieve the raw transaction JSON through your backend or an API client using an API key authorized to read that merchant's transactions.
Save the returned transaction ID, then call GET /v1/transactions/{transaction_id}. The top-level metadata belongs to the current event; history[].metadata belongs to each earlier event. For one specific operation, call GET /v1/transactions/event/{event_id} using the event ID sent with that operation.
For example, from your backend or a local API client with KOARD_API_KEY set securely:
KOARD_BASE_URL="https://api.uat.koard.com"
TRANSACTION_ID="your-transaction-id"
curl --fail-with-body --silent --show-error \
"$KOARD_BASE_URL/v1/transactions/$TRANSACTION_ID" \
-H "x-koard-apikey: $KOARD_API_KEY" \
-H "Accept: application/json"
For Production, use https://api.koard.com and a production key. A transaction ID and key must belong to the same environment and authorized account scope.
A response after a refund may contain these fields (other transaction fields omitted):
{
"transaction_id": "your-transaction-id",
"event_id": "refund-event-id",
"transaction_type": "refund",
"metadata": {
"order_id": "order-123",
"return_id": "return-456",
"reason": "customer_return"
},
"history": [
{
"event_id": "sale-event-id",
"metadata": {
"order_id": "order-123",
"channel": "mobile"
}
}
]
}
If a later event has metadata: null, check the original event in history or use its event lookup; null at the top level does not mean the sale's metadata was deleted. Likewise, older transactions and requests that omitted metadata can legitimately return null.
To display metadata inside your app, have your backend retrieve it and return the fields your UI needs. Do not assume the SDK demo receipt or MMS transaction screen displays arbitrary metadata; the raw API response is the verification path described here.
Verify in UAT and Production
- Send a sale or preauth with a recognizable
order_idand retain its event ID and returned transaction ID. - Retrieve that event in the same environment and confirm the metadata values and JSON types.
- If testing a refund, pass a separate
return_idand retain the sale'sorder_idexplicitly. Retrieve the refund event and the transaction history to compare them. - Reinitializing or deinitializing the SDK does not erase metadata already stored in Koard. Log in again before continuing SDK operations.

