> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tagada.io/llms.txt
> Use this file to discover all available pages before exploring further.

# With the Plugin SDK

> Build a Tagada-hosted telehealth storefront: quiz, checkout, case submission, and patient portal — deployed on Tagada infrastructure.

# Tagada Rx with the Plugin SDK

The [Plugin SDK](/developer-tools/sdk/introduction) is the fastest way to ship an Rx storefront: your React app is deployed **on Tagada infrastructure** (custom domain, instant swap, A/B testing) and gets checkout, payments, and session plumbing for free. The reference implementation is the **TGD Care** template — live at [tgdcare.com](https://tgdcare.com). Two mono-vertical archetypes are also available: **Cobalt** (plan-first, [cobalt.tgdcare.com](https://cobalt.tgdcare.com)) and **Arbor** (quiz-first, [arbor.tgdcare.com](https://arbor.tgdcare.com)) — see [Storefront templates](/developer-tools/rx/storefront-templates) for the full catalog and how each maps onto the shared funnel skeleton. `oakwell.tgdcare.com` is a parked reskin until it is remounted.

<Info>
  **Why Plugin SDK for Rx?** Multi-instance deploys (one plugin, many affiliate subdomains), integrated funnel engine, and the `apiService` that already knows the environment's API base URL and the customer's CMS session — which the patient portal needs. If you're integrating Rx into an existing external site instead, use the [Headless SDK](/developer-tools/rx/headless-sdk).
</Info>

***

## The pattern

The plugin SDK doesn't ship an `rx` namespace — you wrap its `apiService` with two tiny clients (copy them from the Oakwell template):

```
src/lib/rx-client.ts       ← public storefront calls  (/api/public/v1/rx/*)
src/lib/portal-client.ts   ← patient portal calls     (/api/v1/rx/portal/*)
```

```tsx theme={null}
import { useTagada } from '@tagadapay/plugin-sdk/react';
import { createRxClient } from '@/lib/rx-client';
import { createPortalClient } from '@/lib/portal-client';

function useRx() {
  const { apiService } = useTagada();
  return useMemo(() => createRxClient(apiService), [apiService]);
}
```

* `createRxClient` calls the **public** endpoints with `skipAuth: true` — they authorize per-resource via `(orderId, checkoutToken)`.
* `createPortalClient` calls the **portal** endpoints with normal auth — the SDK automatically attaches the customer's `x-cms-token` once they've logged in via the OTP flow (`useLogin`).

***

## 1. Gate the product behind the quiz

```tsx theme={null}
const rx = useRx();

const { required, requiresIdVerification } =
  await rx.isRequiredForProduct(productId);

if (required) {
  // route to the intake quiz before checkout
}
```

## 2. Authorize at checkout, submit the case on thank-you

Payment is standard Plugin SDK (`useCheckout`, `usePayment`) with **`mode: 'auth'`**. That places a hold — it does not capture. On success, park identity in `sessionStorage` and send the patient to `/thank-you` to complete the network questionnaire, then `submitCase`. Approve later captures the hold; decline voids it.

```tsx theme={null}
const { checkoutSession } = useCheckout();

await processCardPayment(checkoutSession.id, card, {
  paymentFlowId,              // from your plugin config
  mode: 'auth',
  onPaymentSuccess: async ({ order }) => {
    saveRxPendingIntake({
      orderId: order.id,
      checkoutToken: checkoutSession.checkoutToken,
      productId,
      patient,
      quizAnswers,
    });
    navigate(`/thank-you?orderId=${order.id}&checkoutToken=${checkoutSession.checkoutToken}`);
  },
});
```

<Note>
  **Multiselect answers:** join them into one comma-separated string before submitting (`{ questionId: 'goals', value: 'lose_weight,improve_sleep' }`). The networks only accept string/number/boolean values.
</Note>

## 3. Live status on the thank-you page

```tsx theme={null}
const { cases } = await rx.getCasesForOrder({ orderId, checkoutToken });
// poll every ~10s: requested → submitted → in_review → approved → shipped
```

## 4. The patient portal

Oakwell ships a complete portal page (`/portal`) you can lift as-is:

```tsx theme={null}
import { useLogin } from '@tagadapay/plugin-sdk/react';

// 1. OTP login — creates the CMS session the portal API needs
const { sendOtp, verifyOtp } = useLogin();

// 2. Then the portal client just works
const portal = createPortalClient(apiService);
const { cases } = await portal.listCases();
const { messages } = await portal.listMessages(caseId);
await portal.sendMessage(caseId, { text, attachments });
await portal.uploadIdentity(caseId, fileToAttachment(file));
```

Features you get by copying the template page: treatments list, case timeline, secure message thread with file attachments, and photo-ID upload for `requiresIdVerification` offerings. See [Patient portal](/developer-tools/rx/patient-portal) for the API contract.

***

## Starting from the Oakwell template

The fastest path is to fork the template rather than build from scratch:

```
templates/v2/telehealth-oakwell/
├── config/default.config.json    ← products, paymentFlowId, branding
├── src/pages/quiz.tsx            ← branching intake
├── src/components/checkout/      ← payment + case submission
├── src/pages/thank-you.tsx       ← status polling + portal link
├── src/pages/portal.tsx          ← full patient portal
└── src/lib/{rx-client,portal-client}.ts
```

Key config knobs in `default.config.json`:

```json theme={null}
{
  "checkout": {
    "productId": "product_xxx",
    "variantId": "variant_xxx",
    "paymentFlowId": "flow_xxx"
  }
}
```

Deploy with the standard plugin pipeline (`plugins/v2` build → deploy → swap onto your domain). In **affiliate mode**, each affiliate brand is one plugin instance on its own marketplace subdomain — same code, different config.
