> For the complete documentation index, see [llms.txt](https://bify.gitbook.io/rwa-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bify.gitbook.io/rwa-docs/bify-sdk/certificates-rewards.md).

# Certificates and Rewards

## Certificate flow

1. The partner creates a BIFY checkout session.
2. BIFY verifies the USDC payment and settles the purchase.
3. BIFY signs a one-time certificate voucher.
4. The customer supplies the recipient wallet.
5. The customer mints the non-transferable certificate.
6. BIFY confirms the transaction and emits `certificate.minted`.

The certificate proves a settled purchase or configured entitlement. It does not represent ownership of the physical product.

## Retrieve or publicly verify a certificate

Use the authenticated method for partner operations. Use the public method for a customer-facing verification page or QR destination.

```ts
const privateCertificate = await bify.certificates.retrieve(certificateId);
const publicCertificate = await bify.certificates.verify(certificateId);

console.log({
  status: publicCertificate.status,
  owner: publicCertificate.owner,
  verificationUrl: publicCertificate.verificationUrl,
});
```

Public certificate responses do not contain customer contact, delivery, or voucher-signing data.

## Mint from a signed voucher

The public claim response contains a one-time voucher and signature after the purchase is settled. Pass those values to the browser certificate component.

```ts
import { mountBifyCertificate } from "@bify/commerce-widget";

const certificateWidget = mountBifyCertificate({
  element: "#bify-certificate",
  voucher: certificate.voucher,
  controllerAddress: contracts.productCertificateController,
  publicApiBaseUrl: "https://api.bify.io",
  sessionId,
  clientToken,
  onComplete: ({ transactionHash }) => {
    showReceipt("Certificate transaction submitted", transactionHash);
  },
  onState: (state, message) => updateClaimStatus(state, message),
});
```

The widget requests the wallet, checks that it matches the voucher buyer, submits the signed voucher transaction to the certificate controller, waits for the receipt, and confirms the transaction with BIFY. The server must use the confirmed transaction and the `certificate.minted` event as the durable state.

## Reward flow

Reward campaigns can provide merch, discounts, exclusive access, or partner perks. BIFY checks settled purchase status, claimant, holding period, claim window, campaign limits, and one-time claim state. Physical fulfilment remains the partner's responsibility.

## Create a campaign

```ts
const campaign = await bify.rewards.create({
  id: "hoodie-holder-reward-2026",
  externalProductId: "hoodie-001",
  rewardType: "merch",
  metadataURI: "https://merchant.example/rewards/hoodie-holder.json",
  metadataHash: "0x6666666666666666666666666666666666666666666666666666666666666666",
  certificateRequired: true,
  minimumHoldDurationSeconds: 60 * 60 * 24 * 180,
  maxClaims: "100",
});
```

## Create and claim a reward

The claim endpoint requires the partner API key. The partner should bind the claim to an authenticated customer flow and must not accept an arbitrary purchase ID and claimant from an unauthenticated browser request.

```ts
const claim = await bify.rewards.claim({
  campaignId: campaign.id,
  purchaseId: purchaseIdFromBify,
  claimant: customerWallet,
  idempotencyKey: `reward:${campaign.id}:${purchaseIdFromBify}:${customerWallet}`,
});
```

Render the signed claim voucher in the browser:

```ts
import { mountBifyRewardClaim } from "@bify/commerce-widget";

mountBifyRewardClaim({
  element: "#bify-reward",
  voucher: claim.voucher,
  controllerAddress: contracts.rewardClaimController,
  confirmationUrl: `https://api.bify.io/v1/public/rewards/claims/${claim.claimId}/confirm`,
  onComplete: ({ transactionHash }) => fulfilPhysicalReward(transactionHash),
  onState: (state, message) => updateClaimStatus(state, message),
});
```

The reward controller enforces campaign status, purchase settlement, certificate requirements, holding period, claim window, maximum claims, wallet identity, voucher expiry, signer authorization, and one-time claim state.

After the browser submits the transaction, the partner can reconcile the claim with the SDK:

```ts
const confirmedClaim = await bify.rewards.confirmClaim({
  claimId: claim.claimId,
  transactionHash,
});

if (confirmedClaim.status === "claimed") {
  await fulfilPhysicalReward(confirmedClaim);
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://bify.gitbook.io/rwa-docs/bify-sdk/certificates-rewards.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
