{"templateId":"markdown","versions":[{"version":"2.0","label":"v2.0","link":"/percents-api/example-integrations/code-examples","default":true,"active":true,"folderId":"27d36c3a"}],"sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["code-group"]},"type":"markdown"},"seo":{"title":"Code Examples","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"code-examples","__idx":0},"children":["Code Examples"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These examples use the same Percents API sandbox scenario across TypeScript, Go, Java, and C#."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A cardholder group (CHG) is a collection of cardholders that Percents treats as one reward entity. Cardholders in the same CHG share merchant activation, cashback, and branded balance state, so they can earn rewards and spend branded balance as if they were one."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Base URL: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://sandbox.percents.com"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["API authentication header: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorization: token tok_11111111-1111-4111-8111-111111111111:api_test_secret"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Cardholder group id: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["chg_22222222-2222-4222-8222-222222222222"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Merchant id: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mp_33333333-3333-4333-8333-333333333333"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Offer id: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mpo_44444444-4444-4444-8444-444444444444"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Card authorization event id: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["issuer-auth-0001"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Earn settlement id: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["issuer-txn-0001"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Spend settlement id: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["issuer-txn-0002"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantId"]}," field on ingested card authorization event and settled transaction event payloads is normal transaction data representing the merchant identifier from the ISO 8583 transaction message. In sandbox only, Percents overloads ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantId"]}," as a forced-match input: pass the Percents ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mp_"]}," merchant partner id for the merchant the transaction should match. It is separate from API authentication."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The flow is:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Register a cardholder group."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Fetch merchants and offers."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Activate the merchant through an offer."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Ingest a card authorization event and settled transaction."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Process the expected ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["qualified_auth"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["qualified_settlement"]}," webhooks."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Ingest a follow-up settled transaction at the same merchant after branded balance is available."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Process the expected ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["qualified_settlement"]}," webhook containing a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["spend"]}," reward effect."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read balances or transaction reward detail for reconciliation."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhook payload shapes, signing, and retry behavior are documented on ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/percents-api/auth-security/webhooks"},"children":["Webhooks"]},". The examples below show where an integration should wait for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["qualified_auth"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["qualified_settlement"]}," events."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"simplified-client-examples","__idx":1},"children":["Simplified Client Examples"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These examples use simplified client method names to make the flow readable. Use the full HTTP examples or generated API reference for exact request shape until official SDKs are published."]},{"$$mdtype":"Tag","name":"CodeGroup","attributes":{"mode":"tabs"},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","data-title":"TypeScript","header":{"title":"TypeScript","controls":{"copy":{}}},"source":"const client = createExamplePercentsClient({\n  baseUrl: 'https://sandbox.percents.com',\n  authorization: 'token tok_11111111-1111-4111-8111-111111111111:api_test_secret',\n});\n\nconst cardholderGroup = await client.cardholderGroups.register({\n  externalId: 'issuer-group-001',\n  defaultLanguage: 'en',\n  cardholders: [\n    {\n      externalId: 'issuer-cardholder-001',\n      cards: [\n        {\n          externalId: 'issuer-card-001',\n          last4: '4242',\n          network: 'visa',\n          physical: true,\n          billingZip: '10001',\n        },\n      ],\n    },\n  ],\n});\n\nconst merchants = await client.merchants.listForCardholderGroup(cardholderGroup.id);\nconst merchant = merchants.find((row) => row.id === 'mp_33333333-3333-4333-8333-333333333333');\n\nconst offers = await client.offers.listForCardholderGroup({\n  chgId: cardholderGroup.id,\n  merchantId: merchant.id,\n});\n\nawait client.activations.activateMerchantOffer({\n  chgId: cardholderGroup.id,\n  merchantId: merchant.id,\n  offerId: offers[0].id,\n});\n\nawait client.transactions.ingestAuthorization({\n  id: 'issuer-auth-0001',\n  amount: 5000,\n  currency: 'usd',\n  cardholderId: 'issuer-cardholder-001',\n  cardId: 'issuer-card-001',\n  merchantId: merchant.id,\n  state: 'approved',\n});\n\nawait client.webhooks.expect('qualified_auth', {\n  reasonCode: 'earn',\n  timing: 'preview',\n});\n\nawait client.transactions.ingestSettlement({\n  id: 'issuer-txn-0001',\n  externalAuthId: 'issuer-auth-0001',\n  amount: 5000,\n  currency: 'usd',\n  cardholderId: 'issuer-cardholder-001',\n  cardId: 'issuer-card-001',\n  merchantId: merchant.id,\n  state: 'settled',\n});\n\nawait client.webhooks.expect('qualified_settlement', {\n  reasonCode: 'earn',\n  timing: 'posted',\n});\n\nconst balance = await client.balances.getForMerchant({\n  chgId: cardholderGroup.id,\n  merchantId: merchant.id,\n});\n\nif (balance.canSpendNow) {\n  await client.transactions.ingestSettlement({\n    id: 'issuer-txn-0002',\n    amount: 1000,\n    currency: 'usd',\n    cardholderId: 'issuer-cardholder-001',\n    cardId: 'issuer-card-001',\n    merchantId: merchant.id,\n    state: 'settled',\n  });\n\n  await client.webhooks.expect('qualified_settlement', {\n    reasonCode: 'spend',\n    timing: 'posted',\n  });\n}\n\nawait client.reconciliation.getTransactionRewardDetail({\n  chgId: cardholderGroup.id,\n  txnId: 'txn_66666666-6666-4666-8666-666666666666',\n});\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","data-title":"Python","header":{"title":"Python","controls":{"copy":{}}},"source":"client = ExamplePercentsClient(\n    base_url=\"https://sandbox.percents.com\",\n    authorization=\"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\",\n)\n\ncardholder_group = client.cardholder_groups.register({\n    \"externalId\": \"issuer-group-001\",\n    \"defaultLanguage\": \"en\",\n    \"cardholders\": [{\n        \"externalId\": \"issuer-cardholder-001\",\n        \"cards\": [{\"externalId\": \"issuer-card-001\", \"last4\": \"4242\", \"network\": \"visa\", \"physical\": True, \"billingZip\": \"10001\"}],\n    }],\n})\n\nmerchants = client.merchants.list_for_cardholder_group(cardholder_group[\"id\"])\nmerchant = next(row for row in merchants if row[\"id\"] == \"mp_33333333-3333-4333-8333-333333333333\")\n\noffers = client.offers.list_for_cardholder_group(\n    chg_id=cardholder_group[\"id\"],\n    merchant_id=merchant[\"id\"],\n)\n\nclient.activations.activate_merchant_offer(\n    chg_id=cardholder_group[\"id\"],\n    merchant_id=merchant[\"id\"],\n    offer_id=offers[0][\"id\"],\n)\n\nclient.transactions.ingest_authorization({\n    \"id\": \"issuer-auth-0001\",\n    \"amount\": 5000,\n    \"currency\": \"usd\",\n    \"cardholderId\": \"issuer-cardholder-001\",\n    \"cardId\": \"issuer-card-001\",\n    \"merchantId\": merchant[\"id\"],\n    \"state\": \"approved\",\n})\n\nclient.webhooks.expect(\"qualified_auth\", reason_code=\"earn\", timing=\"preview\")\n\nclient.transactions.ingest_settlement({\n    \"id\": \"issuer-txn-0001\",\n    \"externalAuthId\": \"issuer-auth-0001\",\n    \"amount\": 5000,\n    \"currency\": \"usd\",\n    \"cardholderId\": \"issuer-cardholder-001\",\n    \"cardId\": \"issuer-card-001\",\n    \"merchantId\": merchant[\"id\"],\n    \"state\": \"settled\",\n})\n\nclient.webhooks.expect(\"qualified_settlement\", reason_code=\"earn\", timing=\"posted\")\n\nbalance = client.balances.get_for_merchant(chg_id=cardholder_group[\"id\"], merchant_id=merchant[\"id\"])\nif balance[\"canSpendNow\"]:\n    client.transactions.ingest_settlement({\n        \"id\": \"issuer-txn-0002\",\n        \"amount\": 1000,\n        \"currency\": \"usd\",\n        \"cardholderId\": \"issuer-cardholder-001\",\n        \"cardId\": \"issuer-card-001\",\n        \"merchantId\": merchant[\"id\"],\n        \"state\": \"settled\",\n    })\n    client.webhooks.expect(\"qualified_settlement\", reason_code=\"spend\", timing=\"posted\")\n\nclient.reconciliation.get_transaction_reward_detail(\n    chg_id=cardholder_group[\"id\"],\n    txn_id=\"txn_66666666-6666-4666-8666-666666666666\",\n)\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"go","data-title":"Go","header":{"title":"Go","controls":{"copy":{}}},"source":"ctx := context.Background()\nclient := NewExamplePercentsClient(ExamplePercentsConfig{\n\tBaseURL:       \"https://sandbox.percents.com\",\n\tAuthorization: \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\",\n})\n\ncardholderGroup, err := client.CardholderGroups.Register(ctx, map[string]any{\n\t\"externalId\":      \"issuer-group-001\",\n\t\"defaultLanguage\": \"en\",\n\t\"cardholders\": []map[string]any{{\n\t\t\"externalId\": \"issuer-cardholder-001\",\n\t\t\"cards\": []map[string]any{{\n\t\t\t\"externalId\": \"issuer-card-001\",\n\t\t\t\"last4\":      \"4242\",\n\t\t\t\"network\":    \"visa\",\n\t\t\t\"physical\":   true,\n\t\t\t\"billingZip\": \"10001\",\n\t\t}},\n\t}},\n})\nif err != nil {\n\treturn err\n}\n\nmerchants, err := client.Merchants.ListForCardholderGroup(ctx, cardholderGroup.ID)\nif err != nil {\n\treturn err\n}\nmerchant := findMerchant(merchants, \"mp_33333333-3333-4333-8333-333333333333\")\n\noffers, err := client.Offers.ListForCardholderGroup(ctx, cardholderGroup.ID, merchant.ID)\nif err != nil {\n\treturn err\n}\n\nerr = client.Activations.ActivateMerchantOffer(ctx, cardholderGroup.ID, merchant.ID, offers[0].ID)\nif err != nil {\n\treturn err\n}\n\nerr = client.Transactions.IngestAuthorization(ctx, map[string]any{\n\t\"id\":           \"issuer-auth-0001\",\n\t\"amount\":       5000,\n\t\"currency\":     \"usd\",\n\t\"cardholderId\": \"issuer-cardholder-001\",\n\t\"cardId\":       \"issuer-card-001\",\n\t\"merchantId\":   merchant.ID,\n\t\"state\":        \"approved\",\n})\nif err != nil {\n\treturn err\n}\nclient.Webhooks.Expect(ctx, \"qualified_auth\", \"earn\", \"preview\")\n\nerr = client.Transactions.IngestSettlement(ctx, map[string]any{\n\t\"id\":             \"issuer-txn-0001\",\n\t\"externalAuthId\": \"issuer-auth-0001\",\n\t\"amount\":         5000,\n\t\"currency\":       \"usd\",\n\t\"cardholderId\":   \"issuer-cardholder-001\",\n\t\"cardId\":         \"issuer-card-001\",\n\t\"merchantId\":     merchant.ID,\n\t\"state\":          \"settled\",\n})\nif err != nil {\n\treturn err\n}\nclient.Webhooks.Expect(ctx, \"qualified_settlement\", \"earn\", \"posted\")\n\nbalance, err := client.Balances.GetForMerchant(ctx, cardholderGroup.ID, merchant.ID)\nif err != nil {\n\treturn err\n}\nif balance.CanSpendNow {\n\terr = client.Transactions.IngestSettlement(ctx, map[string]any{\n\t\t\"id\":           \"issuer-txn-0002\",\n\t\t\"amount\":       1000,\n\t\t\"currency\":     \"usd\",\n\t\t\"cardholderId\": \"issuer-cardholder-001\",\n\t\t\"cardId\":       \"issuer-card-001\",\n\t\t\"merchantId\":   merchant.ID,\n\t\t\"state\":        \"settled\",\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\tclient.Webhooks.Expect(ctx, \"qualified_settlement\", \"spend\", \"posted\")\n}\n\n_, err = client.Reconciliation.GetTransactionRewardDetail(ctx, cardholderGroup.ID, \"txn_66666666-6666-4666-8666-666666666666\")\nreturn err\n","lang":"go"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","data-title":"Java","header":{"title":"Java","controls":{"copy":{}}},"source":"ExamplePercentsClient client = new ExamplePercentsClient(\n    \"https://sandbox.percents.com\",\n    \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\"\n);\n\nCardholderGroup cardholderGroup = client.cardholderGroups().register(Payload.of(\n    \"externalId\", \"issuer-group-001\",\n    \"defaultLanguage\", \"en\",\n    \"cardholders\", List.of(Payload.of(\n        \"externalId\", \"issuer-cardholder-001\",\n        \"cards\", List.of(Payload.of(\"externalId\", \"issuer-card-001\", \"last4\", \"4242\", \"network\", \"visa\", \"physical\", true, \"billingZip\", \"10001\"))\n    ))\n));\n\nMerchant merchant = client.merchants()\n    .listForCardholderGroup(cardholderGroup.id())\n    .stream()\n    .filter(row -> row.id().equals(\"mp_33333333-3333-4333-8333-333333333333\"))\n    .findFirst()\n    .orElseThrow();\n\nOffer offer = client.offers()\n    .listForCardholderGroup(cardholderGroup.id(), merchant.id())\n    .get(0);\n\nclient.activations().activateMerchantOffer(cardholderGroup.id(), merchant.id(), offer.id());\n\nclient.transactions().ingestAuthorization(Payload.of(\n    \"id\", \"issuer-auth-0001\",\n    \"amount\", 5000,\n    \"currency\", \"usd\",\n    \"cardholderId\", \"issuer-cardholder-001\",\n    \"cardId\", \"issuer-card-001\",\n    \"merchantId\", merchant.id(),\n    \"state\", \"approved\"\n));\nclient.webhooks().expect(\"qualified_auth\", \"earn\", \"preview\");\n\nclient.transactions().ingestSettlement(Payload.of(\n    \"id\", \"issuer-txn-0001\",\n    \"externalAuthId\", \"issuer-auth-0001\",\n    \"amount\", 5000,\n    \"currency\", \"usd\",\n    \"cardholderId\", \"issuer-cardholder-001\",\n    \"cardId\", \"issuer-card-001\",\n    \"merchantId\", merchant.id(),\n    \"state\", \"settled\"\n));\nclient.webhooks().expect(\"qualified_settlement\", \"earn\", \"posted\");\n\nMerchantBalance balance = client.balances().getForMerchant(cardholderGroup.id(), merchant.id());\nif (balance.canSpendNow()) {\n  client.transactions().ingestSettlement(Payload.of(\n      \"id\", \"issuer-txn-0002\",\n      \"amount\", 1000,\n      \"currency\", \"usd\",\n      \"cardholderId\", \"issuer-cardholder-001\",\n      \"cardId\", \"issuer-card-001\",\n      \"merchantId\", merchant.id(),\n      \"state\", \"settled\"\n  ));\n  client.webhooks().expect(\"qualified_settlement\", \"spend\", \"posted\");\n}\n\nclient.reconciliation().getTransactionRewardDetail(\n    cardholderGroup.id(),\n    \"txn_66666666-6666-4666-8666-666666666666\"\n);\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"csharp","data-title":"C#","header":{"title":"C#","controls":{"copy":{}}},"source":"var client = new ExamplePercentsClient(\n    baseUrl: \"https://sandbox.percents.com\",\n    authorization: \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\");\n\nvar cardholderGroup = await client.CardholderGroups.RegisterAsync(new {\n    externalId = \"issuer-group-001\",\n    defaultLanguage = \"en\",\n    cardholders = new[] {\n        new {\n            externalId = \"issuer-cardholder-001\",\n            cards = new[] {\n                new { externalId = \"issuer-card-001\", last4 = \"4242\", network = \"visa\", physical = true, billingZip = \"10001\" },\n            },\n        },\n    },\n});\n\nvar merchant = (await client.Merchants.ListForCardholderGroupAsync(cardholderGroup.Id))\n    .Single(row => row.Id == \"mp_33333333-3333-4333-8333-333333333333\");\nvar offer = (await client.Offers.ListForCardholderGroupAsync(cardholderGroup.Id, merchant.Id)).First();\n\nawait client.Activations.ActivateMerchantOfferAsync(cardholderGroup.Id, merchant.Id, offer.Id);\n\nawait client.Transactions.IngestAuthorizationAsync(new {\n    id = \"issuer-auth-0001\", amount = 5000, currency = \"usd\",\n    cardholderId = \"issuer-cardholder-001\", cardId = \"issuer-card-001\",\n    merchantId = merchant.Id, state = \"approved\",\n});\nawait client.Webhooks.ExpectAsync(\"qualified_auth\", reasonCode: \"earn\", timing: \"preview\");\n\nawait client.Transactions.IngestSettlementAsync(new {\n    id = \"issuer-txn-0001\", externalAuthId = \"issuer-auth-0001\", amount = 5000, currency = \"usd\",\n    cardholderId = \"issuer-cardholder-001\", cardId = \"issuer-card-001\",\n    merchantId = merchant.Id, state = \"settled\",\n});\nawait client.Webhooks.ExpectAsync(\"qualified_settlement\", reasonCode: \"earn\", timing: \"posted\");\n\nvar balance = await client.Balances.GetForMerchantAsync(cardholderGroup.Id, merchant.Id);\nif (balance.CanSpendNow) {\n    await client.Transactions.IngestSettlementAsync(new {\n        id = \"issuer-txn-0002\", amount = 1000, currency = \"usd\",\n        cardholderId = \"issuer-cardholder-001\", cardId = \"issuer-card-001\",\n        merchantId = merchant.Id, state = \"settled\",\n    });\n    await client.Webhooks.ExpectAsync(\"qualified_settlement\", reasonCode: \"spend\", timing: \"posted\");\n}\n\nawait client.Reconciliation.GetTransactionRewardDetailAsync(\n    cardholderGroup.Id,\n    \"txn_66666666-6666-4666-8666-666666666666\");\n","lang":"csharp"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","data-title":"Ruby","header":{"title":"Ruby","controls":{"copy":{}}},"source":"client = ExamplePercentsClient.new(\n  base_url: \"https://sandbox.percents.com\",\n  authorization: \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\"\n)\n\ncardholder_group = client.cardholder_groups.register(\n  externalId: \"issuer-group-001\",\n  defaultLanguage: \"en\",\n  cardholders: [\n    {\n      externalId: \"issuer-cardholder-001\",\n      cards: [{ externalId: \"issuer-card-001\", last4: \"4242\", network: \"visa\", physical: true, billingZip: \"10001\" }]\n    }\n  ]\n)\n\nmerchants = client.merchants.list_for_cardholder_group(cardholder_group[:id])\nmerchant = merchants.find { |row| row[:id] == \"mp_33333333-3333-4333-8333-333333333333\" }\noffers = client.offers.list_for_cardholder_group(chg_id: cardholder_group[:id], merchant_id: merchant[:id])\n\nclient.activations.activate_merchant_offer(\n  chg_id: cardholder_group[:id],\n  merchant_id: merchant[:id],\n  offer_id: offers.first[:id]\n)\n\nclient.transactions.ingest_authorization(\n  id: \"issuer-auth-0001\",\n  amount: 5000,\n  currency: \"usd\",\n  cardholderId: \"issuer-cardholder-001\",\n  cardId: \"issuer-card-001\",\n  merchantId: merchant[:id],\n  state: \"approved\"\n)\nclient.webhooks.expect(\"qualified_auth\", reason_code: \"earn\", timing: \"preview\")\n\nclient.transactions.ingest_settlement(\n  id: \"issuer-txn-0001\",\n  externalAuthId: \"issuer-auth-0001\",\n  amount: 5000,\n  currency: \"usd\",\n  cardholderId: \"issuer-cardholder-001\",\n  cardId: \"issuer-card-001\",\n  merchantId: merchant[:id],\n  state: \"settled\"\n)\nclient.webhooks.expect(\"qualified_settlement\", reason_code: \"earn\", timing: \"posted\")\n\nbalance = client.balances.get_for_merchant(chg_id: cardholder_group[:id], merchant_id: merchant[:id])\nif balance[:canSpendNow]\n  client.transactions.ingest_settlement(\n    id: \"issuer-txn-0002\",\n    amount: 1000,\n    currency: \"usd\",\n    cardholderId: \"issuer-cardholder-001\",\n    cardId: \"issuer-card-001\",\n    merchantId: merchant[:id],\n    state: \"settled\"\n  )\n  client.webhooks.expect(\"qualified_settlement\", reason_code: \"spend\", timing: \"posted\")\nend\n\nclient.reconciliation.get_transaction_reward_detail(\n  chg_id: cardholder_group[:id],\n  txn_id: \"txn_66666666-6666-4666-8666-666666666666\"\n)\n","lang":"ruby"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"rust","data-title":"Rust","header":{"title":"Rust","controls":{"copy":{}}},"source":"let client = ExamplePercentsClient::new(\n    \"https://sandbox.percents.com\",\n    \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\",\n);\n\nlet cardholder_group = client.cardholder_groups().register(json!({\n    \"externalId\": \"issuer-group-001\",\n    \"defaultLanguage\": \"en\",\n    \"cardholders\": [{\n        \"externalId\": \"issuer-cardholder-001\",\n        \"cards\": [{\"externalId\": \"issuer-card-001\", \"last4\": \"4242\", \"network\": \"visa\", \"physical\": true, \"billingZip\": \"10001\"}]\n    }]\n})).await?;\n\nlet merchants = client.merchants().list_for_cardholder_group(cardholder_group.id()).await?;\nlet merchant = merchants.iter()\n    .find(|row| row.id == \"mp_33333333-3333-4333-8333-333333333333\")\n    .ok_or(\"merchant not found\")?;\n\nlet offers = client.offers()\n    .list_for_cardholder_group(cardholder_group.id(), &merchant.id)\n    .await?;\n\nclient.activations()\n    .activate_merchant_offer(cardholder_group.id(), &merchant.id, &offers[0].id)\n    .await?;\n\nclient.transactions().ingest_authorization(json!({\n    \"id\": \"issuer-auth-0001\",\n    \"amount\": 5000,\n    \"currency\": \"usd\",\n    \"cardholderId\": \"issuer-cardholder-001\",\n    \"cardId\": \"issuer-card-001\",\n    \"merchantId\": merchant.id,\n    \"state\": \"approved\"\n})).await?;\nclient.webhooks().expect(\"qualified_auth\", \"earn\", \"preview\").await?;\n\nclient.transactions().ingest_settlement(json!({\n    \"id\": \"issuer-txn-0001\",\n    \"externalAuthId\": \"issuer-auth-0001\",\n    \"amount\": 5000,\n    \"currency\": \"usd\",\n    \"cardholderId\": \"issuer-cardholder-001\",\n    \"cardId\": \"issuer-card-001\",\n    \"merchantId\": merchant.id,\n    \"state\": \"settled\"\n})).await?;\nclient.webhooks().expect(\"qualified_settlement\", \"earn\", \"posted\").await?;\n\nlet balance = client.balances().get_for_merchant(cardholder_group.id(), &merchant.id).await?;\nif balance.can_spend_now {\n    client.transactions().ingest_settlement(json!({\n        \"id\": \"issuer-txn-0002\",\n        \"amount\": 1000,\n        \"currency\": \"usd\",\n        \"cardholderId\": \"issuer-cardholder-001\",\n        \"cardId\": \"issuer-card-001\",\n        \"merchantId\": merchant.id,\n        \"state\": \"settled\"\n    })).await?;\n    client.webhooks().expect(\"qualified_settlement\", \"spend\", \"posted\").await?;\n}\n\nclient.reconciliation()\n    .get_transaction_reward_detail(cardholder_group.id(), \"txn_66666666-6666-4666-8666-666666666666\")\n    .await?;\n","lang":"rust"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"full-http-examples","__idx":2},"children":["Full HTTP Examples"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These examples use raw HTTP calls. The helper functions keep the flow readable while still showing the actual endpoints."]},{"$$mdtype":"Tag","name":"CodeGroup","attributes":{"mode":"tabs"},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","data-title":"TypeScript HTTP","header":{"title":"TypeScript HTTP","controls":{"copy":{}}},"source":"const baseUrl = 'https://sandbox.percents.com';\nconst authorization = 'token tok_11111111-1111-4111-8111-111111111111:api_test_secret';\n\nasync function request(method: string, path: string, body?: unknown) {\n  const response = await fetch(`${baseUrl}${path}`, {\n    method,\n    headers: { Authorization: authorization, 'Content-Type': 'application/json' },\n    body: body === undefined ? undefined : JSON.stringify(body),\n  });\n  const text = await response.text();\n  if (!response.ok) throw new Error(`${method} ${path} failed: ${response.status} ${text}`);\n  return text ? JSON.parse(text) : undefined;\n}\n\nconst cardholderGroup = await request('POST', '/api/v2/chg', {\n  externalId: 'issuer-group-001',\n  defaultLanguage: 'en',\n  active: true,\n  cardholders: [\n    {\n      externalId: 'issuer-cardholder-001',\n      active: true,\n      cards: [\n        {\n          externalId: 'issuer-card-001',\n          last4: '4242',\n          bin: '411111',\n          network: 'visa',\n          physical: true,\n          billingZip: '10001',\n          active: true,\n        },\n      ],\n    },\n  ],\n  idempotencyKey: 'setup-001',\n});\n\nconst merchants = await request(\n  'GET',\n  `/api/v2/chg/${cardholderGroup.id}/merchants?page=1&pageSize=25`,\n);\nconst merchant = merchants.data.find(\n  (row: { id: string }) => row.id === 'mp_33333333-3333-4333-8333-333333333333',\n);\nconst offers = await request(\n  'GET',\n  `/api/v2/chg/${cardholderGroup.id}/merchant/${merchant.id}/offers?page=1&pageSize=10`,\n);\n\nawait request('PUT', `/api/v2/chg/${cardholderGroup.id}/merchant/${merchant.id}/activation`, {\n  presentedOfferId: offers.data[0].id,\n  source: 'issuer_host_app',\n  idempotencyKey: 'activation-001',\n});\n\nawait request(\n  'POST',\n  '/api/v1/incoming-auth',\n  transactionPayload({\n    id: 'issuer-auth-0001',\n    amount: 5000,\n    merchantId: merchant.id,\n    state: 'approved',\n  }),\n);\n// Expect: qualified_auth webhook with rewardEffects[0].timing === 'preview'.\n\nawait request(\n  'POST',\n  '/api/v1/incoming-txn',\n  transactionPayload({\n    id: 'issuer-txn-0001',\n    externalAuthId: 'issuer-auth-0001',\n    amount: 5000,\n    merchantId: merchant.id,\n    state: 'settled',\n  }),\n);\n// Expect: qualified_settlement webhook with an earn effect and posted timing.\n\nconst balance = await request(\n  'GET',\n  `/api/v2/chg/${cardholderGroup.id}/merchant/${merchant.id}/balance`,\n);\nif (balance.canSpendNow) {\n  await request(\n    'POST',\n    '/api/v1/incoming-txn',\n    transactionPayload({\n      id: 'issuer-txn-0002',\n      amount: 1000,\n      merchantId: merchant.id,\n      state: 'settled',\n    }),\n  );\n  // Expect: qualified_settlement webhook with a spend effect and posted timing.\n}\n\nawait request(\n  'GET',\n  `/api/v2/chg/${cardholderGroup.id}/transaction/txn_66666666-6666-4666-8666-666666666666/reward-detail`,\n);\n\nfunction transactionPayload(input: {\n  id: string;\n  amount: number;\n  merchantId: string;\n  state: string;\n  externalAuthId?: string;\n}) {\n  return {\n    ...input,\n    currency: 'usd',\n    merchantDescriptor: 'Sandbox Merchant',\n    mcc: '5814',\n    createdAt: '2026-07-01T18:42:00.000Z',\n    cardLast4: '4242',\n    network: 'visa',\n    cardholderId: 'issuer-cardholder-001',\n    cardId: 'issuer-card-001',\n    authMethod: 'online',\n    authTimestamp: '2026-07-01T18:42:00.000Z',\n  };\n}\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","data-title":"Python HTTP","header":{"title":"Python HTTP","controls":{"copy":{}}},"source":"import requests\n\nBASE_URL = \"https://sandbox.percents.com\"\nHEADERS = {\n    \"Authorization\": \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\",\n    \"Content-Type\": \"application/json\",\n}\n\ndef request(method, path, json=None):\n    response = requests.request(method, f\"{BASE_URL}{path}\", json=json, headers=HEADERS, timeout=10)\n    response.raise_for_status()\n    return response.json() if response.text else None\n\ndef transaction_payload(**values):\n    return {\n        **values,\n        \"currency\": \"usd\",\n        \"merchantDescriptor\": \"Sandbox Merchant\",\n        \"mcc\": \"5814\",\n        \"createdAt\": \"2026-07-01T18:42:00.000Z\",\n        \"cardLast4\": \"4242\",\n        \"network\": \"visa\",\n        \"cardholderId\": \"issuer-cardholder-001\",\n        \"cardId\": \"issuer-card-001\",\n        \"authMethod\": \"online\",\n        \"authTimestamp\": \"2026-07-01T18:42:00.000Z\",\n    }\n\ncardholder_group = request(\"POST\", \"/api/v2/chg\", {\n    \"externalId\": \"issuer-group-001\",\n    \"defaultLanguage\": \"en\",\n    \"active\": True,\n    \"cardholders\": [{\n        \"externalId\": \"issuer-cardholder-001\",\n        \"active\": True,\n        \"cards\": [{\"externalId\": \"issuer-card-001\", \"last4\": \"4242\", \"bin\": \"411111\", \"network\": \"visa\", \"physical\": True, \"billingZip\": \"10001\", \"active\": True}],\n    }],\n    \"idempotencyKey\": \"setup-001\",\n})\n\nmerchants = request(\"GET\", f\"/api/v2/chg/{cardholder_group['id']}/merchants?page=1&pageSize=25\")\nmerchant = next(row for row in merchants[\"data\"] if row[\"id\"] == \"mp_33333333-3333-4333-8333-333333333333\")\noffers = request(\"GET\", f\"/api/v2/chg/{cardholder_group['id']}/merchant/{merchant['id']}/offers?page=1&pageSize=10\")\n\nrequest(\"PUT\", f\"/api/v2/chg/{cardholder_group['id']}/merchant/{merchant['id']}/activation\", {\n    \"presentedOfferId\": offers[\"data\"][0][\"id\"],\n    \"source\": \"issuer_host_app\",\n    \"idempotencyKey\": \"activation-001\",\n})\n\nrequest(\"POST\", \"/api/v1/incoming-auth\", transaction_payload(\n    id=\"issuer-auth-0001\",\n    amount=5000,\n    merchantId=merchant[\"id\"],\n    state=\"approved\",\n))\n# Expect: qualified_auth webhook with rewardEffects[0].timing == \"preview\".\n\nrequest(\"POST\", \"/api/v1/incoming-txn\", transaction_payload(\n    id=\"issuer-txn-0001\",\n    externalAuthId=\"issuer-auth-0001\",\n    amount=5000,\n    merchantId=merchant[\"id\"],\n    state=\"settled\",\n))\n# Expect: qualified_settlement webhook with an earn effect and posted timing.\n\nbalance = request(\"GET\", f\"/api/v2/chg/{cardholder_group['id']}/merchant/{merchant['id']}/balance\")\nif balance[\"canSpendNow\"]:\n    request(\"POST\", \"/api/v1/incoming-txn\", transaction_payload(\n        id=\"issuer-txn-0002\",\n        amount=1000,\n        merchantId=merchant[\"id\"],\n        state=\"settled\",\n    ))\n    # Expect: qualified_settlement webhook with a spend effect and posted timing.\n\nrequest(\"GET\", f\"/api/v2/chg/{cardholder_group['id']}/transaction/txn_66666666-6666-4666-8666-666666666666/reward-detail\")\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"go","data-title":"Go HTTP","header":{"title":"Go HTTP","controls":{"copy":{}}},"source":"package main\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\nconst baseURL = \"https://sandbox.percents.com\"\nconst authorization = \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\"\n\nfunc main() {\n\tif err := run(); err != nil {\n\t\tpanic(err)\n\t}\n}\n\nfunc run() error {\n\tctx := context.Background()\n\n\tcardholderGroup := request(ctx, \"POST\", \"/api/v2/chg\", map[string]any{\n\t\t\"externalId\":      \"issuer-group-001\",\n\t\t\"defaultLanguage\": \"en\",\n\t\t\"active\":          true,\n\t\t\"cardholders\": []map[string]any{{\n\t\t\t\"externalId\": \"issuer-cardholder-001\",\n\t\t\t\"active\":     true,\n\t\t\t\"cards\": []map[string]any{{\n\t\t\t\t\"externalId\": \"issuer-card-001\",\n\t\t\t\t\"last4\":      \"4242\",\n\t\t\t\t\"bin\":        \"411111\",\n\t\t\t\t\"network\":    \"visa\",\n\t\t\t\t\"physical\":   true,\n\t\t\t\t\"billingZip\": \"10001\",\n\t\t\t\t\"active\":     true,\n\t\t\t}},\n\t\t}},\n\t\t\"idempotencyKey\": \"setup-001\",\n\t})\n\tchgID := cardholderGroup[\"id\"].(string)\n\n\tmerchants := request(ctx, \"GET\", fmt.Sprintf(\"/api/v2/chg/%s/merchants?page=1&pageSize=25\", chgID), nil)\n\tmerchantID := \"mp_33333333-3333-4333-8333-333333333333\"\n\toffers := request(ctx, \"GET\", fmt.Sprintf(\"/api/v2/chg/%s/merchant/%s/offers?page=1&pageSize=10\", chgID, merchantID), nil)\n\tofferID := offers[\"data\"].([]any)[0].(map[string]any)[\"id\"].(string)\n\t_ = merchants\n\n\trequest(ctx, \"PUT\", fmt.Sprintf(\"/api/v2/chg/%s/merchant/%s/activation\", chgID, merchantID), map[string]any{\n\t\t\"presentedOfferId\": offerID,\n\t\t\"source\":           \"issuer_host_app\",\n\t\t\"idempotencyKey\":   \"activation-001\",\n\t})\n\n\trequest(ctx, \"POST\", \"/api/v1/incoming-auth\", transactionPayload(map[string]any{\n\t\t\"id\":         \"issuer-auth-0001\",\n\t\t\"amount\":     5000,\n\t\t\"merchantId\": merchantID,\n\t\t\"state\":      \"approved\",\n\t}))\n\t// Expect: qualified_auth webhook with rewardEffects[0].timing == \"preview\".\n\n\trequest(ctx, \"POST\", \"/api/v1/incoming-txn\", transactionPayload(map[string]any{\n\t\t\"id\":             \"issuer-txn-0001\",\n\t\t\"externalAuthId\": \"issuer-auth-0001\",\n\t\t\"amount\":         5000,\n\t\t\"merchantId\":     merchantID,\n\t\t\"state\":          \"settled\",\n\t}))\n\t// Expect: qualified_settlement webhook with an earn effect and posted timing.\n\n\tbalance := request(ctx, \"GET\", fmt.Sprintf(\"/api/v2/chg/%s/merchant/%s/balance\", chgID, merchantID), nil)\n\tif balance[\"canSpendNow\"].(bool) {\n\t\trequest(ctx, \"POST\", \"/api/v1/incoming-txn\", transactionPayload(map[string]any{\n\t\t\t\"id\":         \"issuer-txn-0002\",\n\t\t\t\"amount\":     1000,\n\t\t\t\"merchantId\": merchantID,\n\t\t\t\"state\":      \"settled\",\n\t\t}))\n\t\t// Expect: qualified_settlement webhook with a spend effect and posted timing.\n\t}\n\n\trequest(ctx, \"GET\", fmt.Sprintf(\"/api/v2/chg/%s/transaction/%s/reward-detail\", chgID, \"txn_66666666-6666-4666-8666-666666666666\"), nil)\n\treturn nil\n}\n\nfunc request(ctx context.Context, method string, path string, payload any) map[string]any {\n\tbody, _ := json.Marshal(payload)\n\tif payload == nil {\n\t\tbody = nil\n\t}\n\treq, _ := http.NewRequestWithContext(ctx, method, baseURL+path, bytes.NewReader(body))\n\treq.Header.Set(\"Authorization\", authorization)\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\tres, err := http.DefaultClient.Do(req)\n\tif err != nil || res.StatusCode < 200 || res.StatusCode >= 300 {\n\t\tpanic(fmt.Sprintf(\"%s %s failed\", method, path))\n\t}\n\tdefer res.Body.Close()\n\tvar result map[string]any\n\tjson.NewDecoder(res.Body).Decode(&result)\n\treturn result\n}\n\nfunc transactionPayload(values map[string]any) map[string]any {\n\tpayload := map[string]any{\n\t\t\"currency\":           \"usd\",\n\t\t\"merchantDescriptor\": \"Sandbox Merchant\",\n\t\t\"mcc\":                \"5814\",\n\t\t\"createdAt\":          \"2026-07-01T18:42:00.000Z\",\n\t\t\"cardLast4\":          \"4242\",\n\t\t\"network\":            \"visa\",\n\t\t\"cardholderId\":       \"issuer-cardholder-001\",\n\t\t\"cardId\":             \"issuer-card-001\",\n\t\t\"authMethod\":         \"online\",\n\t\t\"authTimestamp\":      \"2026-07-01T18:42:00.000Z\",\n\t}\n\tfor key, value := range values {\n\t\tpayload[key] = value\n\t}\n\treturn payload\n}\n","lang":"go"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"java","data-title":"Java HTTP","header":{"title":"Java HTTP","controls":{"copy":{}}},"source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\npublic class PercentsHttpExample {\n  static final String BASE_URL = \"https://sandbox.percents.com\";\n  static final String AUTHORIZATION = \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\";\n  static final HttpClient HTTP = HttpClient.newHttpClient();\n\n  public static void main(String[] args) throws Exception {\n    String cardholderGroup = request(\"POST\", \"/api/v2/chg\", \"\"\"\n      {\"externalId\":\"issuer-group-001\",\"defaultLanguage\":\"en\",\"active\":true,\n       \"cardholders\":[{\"externalId\":\"issuer-cardholder-001\",\"active\":true,\n       \"cards\":[{\"externalId\":\"issuer-card-001\",\"last4\":\"4242\",\"bin\":\"411111\",\"network\":\"visa\",\"physical\":true,\"billingZip\":\"10001\",\"active\":true}]}],\n       \"idempotencyKey\":\"setup-001\"}\n      \"\"\");\n\n    String chgId = \"chg_22222222-2222-4222-8222-222222222222\";\n    String merchantId = \"mp_33333333-3333-4333-8333-333333333333\";\n    request(\"GET\", \"/api/v2/chg/\" + chgId + \"/merchants?page=1&pageSize=25\", null);\n    request(\"GET\", \"/api/v2/chg/\" + chgId + \"/merchant/\" + merchantId + \"/offers?page=1&pageSize=10\", null);\n\n    request(\"PUT\", \"/api/v2/chg/\" + chgId + \"/merchant/\" + merchantId + \"/activation\", \"\"\"\n      {\"presentedOfferId\":\"mpo_44444444-4444-4444-8444-444444444444\",\n       \"source\":\"issuer_host_app\",\"idempotencyKey\":\"activation-001\"}\n      \"\"\");\n\n    request(\"POST\", \"/api/v1/incoming-auth\", transactionPayload(\"issuer-auth-0001\", 5000, merchantId, \"approved\", null));\n    // Expect: qualified_auth webhook with rewardEffects[0].timing == \"preview\".\n\n    request(\"POST\", \"/api/v1/incoming-txn\", transactionPayload(\"issuer-txn-0001\", 5000, merchantId, \"settled\", \"issuer-auth-0001\"));\n    // Expect: qualified_settlement webhook with an earn effect and posted timing.\n\n    String balance = request(\"GET\", \"/api/v2/chg/\" + chgId + \"/merchant/\" + merchantId + \"/balance\", null);\n    if (balance.contains(\"\\\"canSpendNow\\\":true\")) {\n      request(\"POST\", \"/api/v1/incoming-txn\", transactionPayload(\"issuer-txn-0002\", 1000, merchantId, \"settled\", null));\n      // Expect: qualified_settlement webhook with a spend effect and posted timing.\n    }\n\n    request(\"GET\", \"/api/v2/chg/\" + chgId + \"/transaction/txn_66666666-6666-4666-8666-666666666666/reward-detail\", null);\n    if (cardholderGroup.isBlank()) throw new IllegalStateException(\"empty response\");\n  }\n\n  static String request(String method, String path, String body) throws Exception {\n    HttpRequest.Builder builder = HttpRequest.newBuilder(URI.create(BASE_URL + path))\n        .header(\"Authorization\", AUTHORIZATION)\n        .header(\"Content-Type\", \"application/json\");\n    HttpRequest request = body == null\n        ? builder.method(method, HttpRequest.BodyPublishers.noBody()).build()\n        : builder.method(method, HttpRequest.BodyPublishers.ofString(body)).build();\n    HttpResponse<String> response = HTTP.send(request, HttpResponse.BodyHandlers.ofString());\n    if (response.statusCode() < 200 || response.statusCode() >= 300) {\n      throw new IllegalStateException(method + \" \" + path + \" failed: \" + response.statusCode());\n    }\n    return response.body();\n  }\n\n  static String transactionPayload(String id, int amount, String merchantId, String state, String externalAuthId) {\n    String authPart = externalAuthId == null ? \"\" : \"\\\"externalAuthId\\\":\\\"\" + externalAuthId + \"\\\",\";\n    return \"\"\"\n      {\"id\":\"%s\",%s\"amount\":%d,\"currency\":\"usd\",\"merchantDescriptor\":\"Sandbox Merchant\",\n       \"mcc\":\"5814\",\"createdAt\":\"2026-07-01T18:42:00.000Z\",\"cardLast4\":\"4242\",\n       \"network\":\"visa\",\"cardholderId\":\"issuer-cardholder-001\",\"cardId\":\"issuer-card-001\",\n       \"authMethod\":\"online\",\"authTimestamp\":\"2026-07-01T18:42:00.000Z\",\n       \"merchantId\":\"%s\",\"state\":\"%s\"}\n      \"\"\".formatted(id, authPart, amount, merchantId, state);\n  }\n}\n","lang":"java"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"csharp","data-title":"C# HTTP","header":{"title":"C# HTTP","controls":{"copy":{}}},"source":"using System.Net.Http.Json;\nusing System.Text.Json;\n\nvar http = new HttpClient { BaseAddress = new Uri(\"https://sandbox.percents.com\") };\nhttp.DefaultRequestHeaders.Add(\n    \"Authorization\",\n    \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\");\n\nasync Task<JsonElement> RequestAsync(HttpMethod method, string path, object? payload = null)\n{\n    using var request = new HttpRequestMessage(method, path);\n    if (payload is not null) request.Content = JsonContent.Create(payload);\n\n    using var response = await http.SendAsync(request);\n    var content = await response.Content.ReadAsStringAsync();\n    response.EnsureSuccessStatusCode();\n    return string.IsNullOrEmpty(content) ? default : JsonDocument.Parse(content).RootElement.Clone();\n}\n\nobject TransactionPayload(string id, int amount, string merchantId, string state, string? externalAuthId = null) => new {\n    id,\n    externalAuthId,\n    amount,\n    currency = \"usd\",\n    merchantDescriptor = \"Sandbox Merchant\",\n    mcc = \"5814\",\n    createdAt = \"2026-07-01T18:42:00.000Z\",\n    cardLast4 = \"4242\",\n    network = \"visa\",\n    cardholderId = \"issuer-cardholder-001\",\n    cardId = \"issuer-card-001\",\n    authMethod = \"online\",\n    authTimestamp = \"2026-07-01T18:42:00.000Z\",\n    merchantId,\n    state,\n};\n\nvar cardholderGroup = await RequestAsync(HttpMethod.Post, \"/api/v2/chg\", new {\n    externalId = \"issuer-group-001\",\n    defaultLanguage = \"en\",\n    active = true,\n    cardholders = new[] {\n        new {\n            externalId = \"issuer-cardholder-001\",\n            active = true,\n            cards = new[] {\n                new { externalId = \"issuer-card-001\", last4 = \"4242\", bin = \"411111\", network = \"visa\", physical = true, billingZip = \"10001\", active = true },\n            },\n        },\n    },\n    idempotencyKey = \"setup-001\",\n});\n\nvar chgId = cardholderGroup.GetProperty(\"id\").GetString()!;\nvar merchantId = \"mp_33333333-3333-4333-8333-333333333333\";\nawait RequestAsync(HttpMethod.Get, $\"/api/v2/chg/{chgId}/merchants?page=1&pageSize=25\");\nvar offers = await RequestAsync(HttpMethod.Get, $\"/api/v2/chg/{chgId}/merchant/{merchantId}/offers?page=1&pageSize=10\");\nvar offerId = offers.GetProperty(\"data\")[0].GetProperty(\"id\").GetString()!;\n\nawait RequestAsync(HttpMethod.Put, $\"/api/v2/chg/{chgId}/merchant/{merchantId}/activation\", new {\n    presentedOfferId = offerId,\n    source = \"issuer_host_app\",\n    idempotencyKey = \"activation-001\",\n});\n\nawait RequestAsync(HttpMethod.Post, \"/api/v1/incoming-auth\",\n    TransactionPayload(\"issuer-auth-0001\", 5000, merchantId, \"approved\"));\n// Expect: qualified_auth webhook with rewardEffects[0].timing == \"preview\".\n\nawait RequestAsync(HttpMethod.Post, \"/api/v1/incoming-txn\",\n    TransactionPayload(\"issuer-txn-0001\", 5000, merchantId, \"settled\", \"issuer-auth-0001\"));\n// Expect: qualified_settlement webhook with an earn effect and posted timing.\n\nvar balance = await RequestAsync(HttpMethod.Get, $\"/api/v2/chg/{chgId}/merchant/{merchantId}/balance\");\nif (balance.GetProperty(\"canSpendNow\").GetBoolean()) {\n    await RequestAsync(HttpMethod.Post, \"/api/v1/incoming-txn\",\n        TransactionPayload(\"issuer-txn-0002\", 1000, merchantId, \"settled\"));\n    // Expect: qualified_settlement webhook with a spend effect and posted timing.\n}\n\nawait RequestAsync(HttpMethod.Get,\n    $\"/api/v2/chg/{chgId}/transaction/txn_66666666-6666-4666-8666-666666666666/reward-detail\");\n","lang":"csharp"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","data-title":"Ruby HTTP","header":{"title":"Ruby HTTP","controls":{"copy":{}}},"source":"require \"json\"\nrequire \"net/http\"\nrequire \"uri\"\n\nBASE_URL = \"https://sandbox.percents.com\"\nAUTHORIZATION = \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\"\n\ndef request(method, path, body = nil)\n  uri = URI(\"#{BASE_URL}#{path}\")\n  request = Net::HTTP.const_get(method.capitalize).new(uri)\n  request[\"Authorization\"] = AUTHORIZATION\n  request[\"Content-Type\"] = \"application/json\"\n  request.body = body.to_json if body\n\n  response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }\n  raise \"#{method} #{path} failed: #{response.code}\" unless response.is_a?(Net::HTTPSuccess)\n  response.body.empty? ? nil : JSON.parse(response.body)\nend\n\ndef transaction_payload(values)\n  {\n    currency: \"usd\",\n    merchantDescriptor: \"Sandbox Merchant\",\n    mcc: \"5814\",\n    createdAt: \"2026-07-01T18:42:00.000Z\",\n    cardLast4: \"4242\",\n    network: \"visa\",\n    cardholderId: \"issuer-cardholder-001\",\n    cardId: \"issuer-card-001\",\n    authMethod: \"online\",\n    authTimestamp: \"2026-07-01T18:42:00.000Z\"\n  }.merge(values)\nend\n\ncardholder_group = request(\"post\", \"/api/v2/chg\", {\n  externalId: \"issuer-group-001\",\n  defaultLanguage: \"en\",\n  active: true,\n  cardholders: [{\n    externalId: \"issuer-cardholder-001\",\n    active: true,\n    cards: [{ externalId: \"issuer-card-001\", last4: \"4242\", bin: \"411111\", network: \"visa\", physical: true, billingZip: \"10001\", active: true }]\n  }],\n  idempotencyKey: \"setup-001\"\n})\n\nchg_id = cardholder_group[\"id\"]\nmerchants = request(\"get\", \"/api/v2/chg/#{chg_id}/merchants?page=1&pageSize=25\")\nmerchant = merchants[\"data\"].find { |row| row[\"id\"] == \"mp_33333333-3333-4333-8333-333333333333\" }\noffers = request(\"get\", \"/api/v2/chg/#{chg_id}/merchant/#{merchant[\"id\"]}/offers?page=1&pageSize=10\")\n\nrequest(\"put\", \"/api/v2/chg/#{chg_id}/merchant/#{merchant[\"id\"]}/activation\", {\n  presentedOfferId: offers[\"data\"].first[\"id\"],\n  source: \"issuer_host_app\",\n  idempotencyKey: \"activation-001\"\n})\n\nrequest(\"post\", \"/api/v1/incoming-auth\", transaction_payload(\n  id: \"issuer-auth-0001\", amount: 5000, merchantId: merchant[\"id\"], state: \"approved\"\n))\n# Expect: qualified_auth webhook with rewardEffects[0].timing == \"preview\".\n\nrequest(\"post\", \"/api/v1/incoming-txn\", transaction_payload(\n  id: \"issuer-txn-0001\", externalAuthId: \"issuer-auth-0001\", amount: 5000, merchantId: merchant[\"id\"], state: \"settled\"\n))\n# Expect: qualified_settlement webhook with an earn effect and posted timing.\n\nbalance = request(\"get\", \"/api/v2/chg/#{chg_id}/merchant/#{merchant[\"id\"]}/balance\")\nif balance[\"canSpendNow\"]\n  request(\"post\", \"/api/v1/incoming-txn\", transaction_payload(\n    id: \"issuer-txn-0002\", amount: 1000, merchantId: merchant[\"id\"], state: \"settled\"\n  ))\n  # Expect: qualified_settlement webhook with a spend effect and posted timing.\nend\n\nrequest(\"get\", \"/api/v2/chg/#{chg_id}/transaction/txn_66666666-6666-4666-8666-666666666666/reward-detail\")\n","lang":"ruby"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"rust","data-title":"Rust HTTP","header":{"title":"Rust HTTP","controls":{"copy":{}}},"source":"use reqwest::blocking::Client;\nuse serde_json::{json, Value};\n\nconst BASE_URL: &str = \"https://sandbox.percents.com\";\nconst AUTHORIZATION: &str = \"token tok_11111111-1111-4111-8111-111111111111:api_test_secret\";\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let http = Client::new();\n\n    let cardholder_group = request(&http, \"POST\", \"/api/v2/chg\", Some(json!({\n        \"externalId\": \"issuer-group-001\",\n        \"defaultLanguage\": \"en\",\n        \"active\": true,\n        \"cardholders\": [{\n            \"externalId\": \"issuer-cardholder-001\",\n            \"active\": true,\n            \"cards\": [{\"externalId\": \"issuer-card-001\", \"last4\": \"4242\", \"bin\": \"411111\", \"network\": \"visa\", \"physical\": true, \"billingZip\": \"10001\", \"active\": true}]\n        }],\n        \"idempotencyKey\": \"setup-001\"\n    })))?;\n\n    let chg_id = cardholder_group[\"id\"].as_str().unwrap();\n    let merchants = request(&http, \"GET\", &format!(\"/api/v2/chg/{chg_id}/merchants?page=1&pageSize=25\"), None)?;\n    let merchant_id = merchants[\"data\"].as_array().unwrap()\n        .iter()\n        .find(|row| row[\"id\"] == \"mp_33333333-3333-4333-8333-333333333333\")\n        .unwrap()[\"id\"].as_str().unwrap();\n    let offers = request(&http, \"GET\", &format!(\"/api/v2/chg/{chg_id}/merchant/{merchant_id}/offers?page=1&pageSize=10\"), None)?;\n    let offer_id = offers[\"data\"][0][\"id\"].as_str().unwrap();\n\n    request(&http, \"PUT\", &format!(\"/api/v2/chg/{chg_id}/merchant/{merchant_id}/activation\"), Some(json!({\n        \"presentedOfferId\": offer_id,\n        \"source\": \"issuer_host_app\",\n        \"idempotencyKey\": \"activation-001\"\n    })))?;\n\n    request(&http, \"POST\", \"/api/v1/incoming-auth\", Some(transaction_payload(json!({\n        \"id\": \"issuer-auth-0001\",\n        \"amount\": 5000,\n        \"merchantId\": merchant_id,\n        \"state\": \"approved\"\n    }))))?;\n    // Expect: qualified_auth webhook with rewardEffects[0].timing == \"preview\".\n\n    request(&http, \"POST\", \"/api/v1/incoming-txn\", Some(transaction_payload(json!({\n        \"id\": \"issuer-txn-0001\",\n        \"externalAuthId\": \"issuer-auth-0001\",\n        \"amount\": 5000,\n        \"merchantId\": merchant_id,\n        \"state\": \"settled\"\n    }))))?;\n    // Expect: qualified_settlement webhook with an earn effect and posted timing.\n\n    let balance = request(&http, \"GET\", &format!(\"/api/v2/chg/{chg_id}/merchant/{merchant_id}/balance\"), None)?;\n    if balance[\"canSpendNow\"].as_bool().unwrap_or(false) {\n        request(&http, \"POST\", \"/api/v1/incoming-txn\", Some(transaction_payload(json!({\n            \"id\": \"issuer-txn-0002\",\n            \"amount\": 1000,\n            \"merchantId\": merchant_id,\n            \"state\": \"settled\"\n        }))))?;\n        // Expect: qualified_settlement webhook with a spend effect and posted timing.\n    }\n\n    request(&http, \"GET\", &format!(\"/api/v2/chg/{chg_id}/transaction/txn_66666666-6666-4666-8666-666666666666/reward-detail\"), None)?;\n    Ok(())\n}\n\nfn request(client: &Client, method: &str, path: &str, body: Option<Value>) -> Result<Value, Box<dyn std::error::Error>> {\n    let url = format!(\"{BASE_URL}{path}\");\n    let builder = match method {\n        \"GET\" => client.get(&url),\n        \"POST\" => client.post(&url),\n        \"PUT\" => client.put(&url),\n        _ => return Err(\"unsupported method\".into()),\n    }\n    .header(\"Authorization\", AUTHORIZATION)\n    .header(\"Content-Type\", \"application/json\");\n\n    let response = if let Some(value) = body { builder.json(&value).send()? } else { builder.send()? };\n    if !response.status().is_success() {\n        return Err(format!(\"{method} {path} failed: {}\", response.status()).into());\n    }\n    Ok(response.json().unwrap_or_else(|_| json!({})))\n}\n\nfn transaction_payload(values: Value) -> Value {\n    let mut payload = json!({\n        \"currency\": \"usd\",\n        \"merchantDescriptor\": \"Sandbox Merchant\",\n        \"mcc\": \"5814\",\n        \"createdAt\": \"2026-07-01T18:42:00.000Z\",\n        \"cardLast4\": \"4242\",\n        \"network\": \"visa\",\n        \"cardholderId\": \"issuer-cardholder-001\",\n        \"cardId\": \"issuer-card-001\",\n        \"authMethod\": \"online\",\n        \"authTimestamp\": \"2026-07-01T18:42:00.000Z\"\n    });\n    payload.as_object_mut().unwrap().extend(values.as_object().unwrap().clone());\n    payload\n}\n","lang":"rust"},"children":[]}]}]},"headings":[{"value":"Code Examples","id":"code-examples","depth":1},{"value":"Simplified Client Examples","id":"simplified-client-examples","depth":2},{"value":"Full HTTP Examples","id":"full-http-examples","depth":2}],"frontmatter":{"seo":{"title":"Code Examples"}},"lastModified":"2026-07-21T18:22:26.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/percents-api/example-integrations/code-examples","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}