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

# Ayrshare Connect विजेट

> अपने उपयोगकर्ताओं को आपके अपने डैशबोर्ड के अंदर से सोशल अकाउंट लिंक करने दें — एक script टैग और आपके पेज में एम्बेडेड हमारे बटनों के साथ।

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["business"]} maxPackRequired={true} />

Ayrshare Connect विजेट हमारे लिंकिंग बटन **आपके अपने डैशबोर्ड के अंदर** रख देता है। आप एक script
लोड करते हैं, अपने लेआउट में जहाँ कोई नेटवर्क आता है वहाँ एक slot डालते हैं, और हम वहाँ एक ऐसा बटन
render करते हैं जो पहले से दिखाता है कि अकाउंट कनेक्टेड है या नहीं। आपका उपयोगकर्ता उस पर क्लिक
करता है और आपका पेज छोड़े बिना अकाउंट लिंक करता है — अधिकतम एक पॉपअप, वह भी नेटवर्क का अपना।

आपको कोई पॉपअप handling, कोई OAuth callbacks, कोई session refresh, और कोई प्रति-नेटवर्क logic
नहीं लिखनी पड़ती। जब कोई नेटवर्क अपनी तरफ़ कुछ बदलता है, तो फ़िक्स हमारे deploy करते ही हमारे
फ़्रेमों के अंदर पहुँच जाता है; आप कुछ भी redeploy नहीं करते।

## आपको कौन सा सरफ़ेस चाहिए

| सरफ़ेस | आपका उपयोगकर्ता देखता है | व्हाइट-लेबलिंग | इसे कब चुनें |
| - | - | - | - |
| **विजेट — एम्बेडेड फ़्रेम** ([`mount()`](#mount-a-slot)) | हमारे बटन आपके अपने लेआउट में inline; नेटवर्क के अपने पॉपअप तक कोई पॉपअप नहीं | **सबसे मज़बूत।** आपका पेज, आपके फ़ॉन्ट और रंग, और आपका उपयोगकर्ता उसे कभी नहीं छोड़ता | आपके पास प्रति नेटवर्क एक row वाला डैशबोर्ड है और आप चाहते हैं कि लिंकिंग वहीं हो। Max Pack। |
| **विजेट — आपका अपना बटन** ([`popup()`](#your-own-button)) | आपका बटन, फिर नेटवर्क के लिए एक पॉपअप | **मज़बूत।** पॉपअप हमारा है, लेकिन वह संक्षिप्त है और आपका appearance अपनाता है | आप अपना बटन और styling चाहते हैं, और अपने लेआउट में कोई फ़्रेम नहीं। Max Pack। |
| **होस्टेड लिंकिंग पेज** ([कैसे करें](/docs/multiple-users/api-integration-business#single-sign-on-with-jwt-authentication)) | हमारे द्वारा होस्ट किया गया पेज, जिस पर आपका लोगो, रंग और कस्टम CSS होते हैं | **सबसे कमज़ोर।** यह हमारा पेज है, और आपका उपयोगकर्ता इसका उपयोग करने के लिए आपका पेज छोड़ता है | आप बाँटने के लिए एक ही लिंक चाहते हैं, या ईमेल से ऑनबोर्ड कर रहे हैं। कुछ भी बनाने की ज़रूरत नहीं, कोई Max Pack नहीं। |

<div className="my-8">
  <Frame caption="एम्बेडेड फ़्रेम: हमारे tiles आपके अपने लेआउट के अंदर render किए गए, प्रति नेटवर्क एक slot या कई के लिए एक slot।">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-frames.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=d6c26228a418421772f753f31b0dfc59" alt="A customer dashboard with Ayrshare Connect tiles for Instagram, TikTok and LinkedIn embedded in a card" width="2400" height="1120" data-path="images/multiple-users/connect-widget-frames.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="आपका अपना बटन: बटन आप render करते हैं, और हमारा एक संक्षिप्त पॉपअप नेटवर्क को संभालता है।">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-popup.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=b68021d477703d97a5994ee85d299be6" alt="A customer dashboard with its own Connect buttons and an Ayrshare popup showing the Facebook hand-off screen" width="2880" height="1317" data-path="images/multiple-users/connect-widget-popup.webp" />
  </Frame>
</div>

<div className="my-8">
  <Frame caption="होस्टेड लिंकिंग पेज: हमारे द्वारा होस्ट किया गया पेज, आपके लोगो और रंगों के साथ, जिसका उपयोग करने के लिए आपका उपयोगकर्ता आपका ऐप छोड़ता है।">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-hosted.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=4d22a992f6e70808fd9ad9f065ed3879" alt="The hosted social linking page showing every available network" width="2336" height="1906" data-path="images/multiple-users/connect-widget-hosted.webp" />
  </Frame>
</div>

<Note>
  विजेट की दोनों rows **एक ही इंटीग्रेशन** हैं, दो नहीं। एक ही `init` आपको दोनों देता है: जहाँ आप
  हमारे बटन चाहते हैं वहाँ फ़्रेम mount करें, और बाक़ी हर जगह अपने बटन से `popup()` कॉल करें। वे
  एक ही सेशन साझा करते हैं और एक ही handlers पर रिपोर्ट करते हैं।

  [डायरेक्ट मोड](/docs/multiple-users/connect-direct-mode) वही पॉपअप है **बिना** हमारी स्क्रिप्ट के —
  सख़्त Content-Security-Policy वाले पेज, server-rendered पेज, या native ऐप के लिए। वहाँ पॉपअप
  आप स्वयं खोलते और देखते हैं।
</Note>

## Script जोड़ें

कोई version और उसका hash pin करें, या बिना hash के कोई channel track करें। दोनों कभी नहीं —
बदलते रहने वाले URL पर `integrity` attribute हमारी अगली release पर काम करना बंद कर देता है,
क्योंकि वह जिस फ़ाइल का नाम लेता है वह वैध रूप से बदल चुकी होती है।

```html Pinned version theme={"system"}
<script
  src="https://app.ayrshare.com/ayrshare-connect/1.2.0/widget.js"
  integrity="sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz"
  crossorigin="anonymous"
></script>
```

```html Tracking v1 theme={"system"}
<script src="https://app.ayrshare.com/ayrshare-connect/v1/widget.js" crossorigin="anonymous"></script>
```

| पाथ | Caching | `integrity` |
| - | - | - |
| `/ayrshare-connect/<version>/widget.js` | immutable, एक वर्ष | **हाँ** — इसे pin करें |
| `/ayrshare-connect/v1/widget.js` | पाँच मिनट | नहीं |
| `/ayrshare-connect/latest/widget.js` | पाँच मिनट | नहीं |

**`v1` वह channel है जिसकी अनुशंसा की जाती है।** यह फ़िक्स अपनाता है लेकिन कभी breaking change
पार नहीं करता। `latest` परिभाषा से ही major versions पार करता है, इसलिए वह अंततः आपके पेज को ऐसा
version थमा देगा जिसके व्यवहार की आपने समीक्षा नहीं की है।

हर version का hash
[`manifest.json`](https://app.ayrshare.com/ayrshare-connect/manifest.json) में प्रकाशित होता है,
जो यह भी बताता है कि हर channel वर्तमान में क्या serve कर रहा है:

```json manifest.json theme={"system"}
{
  "versions": {
    "1.2.0": { "integrity": "sha384-qQ/of8CnYj26nF9RkQ6qk3D1vvzWesu7mpBK6MFK2D0EmmgRJHfbDmRowFBfltmz" }
  },
  "latest": "1.2.0",
  "channels": { "v1": "1.2.0", "latest": "1.2.0" }
}
```

हर bundle की शुरुआत में भी एक comment होता है जो उसका अपना version बताता है — यह हमें बताने का
सबसे तेज़ तरीक़ा कि कोई पेज वास्तव में क्या चला रहा है:

```js theme={"system"}
/*! ayrshare-connect 1.2.0 */
```

### Content-Security-Policy

यदि आपका पेज Content-Security-Policy भेजता है, तो उसे **दो** entries चाहिए, दोनों उस host के नाम
पर जिससे आप script लोड करते हैं:

```
script-src https://app.ayrshare.com;
frame-src  https://app.ayrshare.com;
```

बस यही पूरी सूची है। हमारे लिए आपको **कोई `connect-src` entry नहीं** चाहिए — हमारे फ़्रेम हमारे
API तक अपने अंदर से पहुँचते हैं, आपके पेज से नहीं — और **पॉपअप के लिए कोई entry नहीं**, जो
top-level windows हैं जिन पर आपकी policy लागू नहीं होती। दोनों entries को मापा गया है:
`script-src` हटाने से script ब्लॉक होती है, और `frame-src` हटाने से फ़्रेम।

## एक instance शुरू करें

`session` एकमात्र आवश्यक option है। इसे **प्रति instance एक बार** कॉल किया जाता है, प्रति mount
एक बार नहीं, और इसे `mode: "connect"` के साथ
[Link Session बनाएँ](/docs/apis/profiles/create-link-session) के प्रति आपके backend की प्रतिक्रिया —
`{ sessionId, token, expiresAt }` — बिल्कुल जैसी आई वैसी लौटानी होगी।

```javascript Your page theme={"system"}
const connect = AyrshareConnect.init({
  session: () => fetch("/my-api/ayrshare-session").then(r => r.json()),
  appearance: { "--ayr-connect-accent": "#0B7A6C" },
  maxHeight: 800,
});
```

```javascript Your backend theme={"system"}
app.get("/my-api/ayrshare-session", async (req, res) => {
  const response = await fetch("https://api.ayrshare.com/api/profiles/link-sessions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${process.env.AYRSHARE_API_KEY}`,
      "Profile-Key": profileKeyFor(req.user),
    },
    body: JSON.stringify({ mode: "connect", origin: "https://app.example.com" }),
  });

  res.json(await response.json());
});
```

विजेट सेशन **कोई** `network` नहीं बताता — यह आपके अकाउंट द्वारा अनुमत हर नेटवर्क को अधिकृत करता
है, और कौन से दिखाई देते हैं यह प्रति mount, आपके पेज में तय होता है। यह एक `origin` ज़रूर ले जाता
है, जो एकमात्र चीज़ है जिससे फ़्रेम render होते ही हैं: फ़्रेम उसे embed करने वाले पेज की तुलना उस
मान से करता है और कहीं और render करने से इनकार कर देता है।

<Warning>
  सेशन अपने server पर बनाएँ। कॉल को आपकी API Key चाहिए, और जो token यह लौटाता है वह आपके
  उपयोगकर्ता को उनके User Profile में साइन इन कर देता है — इसे पासवर्ड की तरह मानें।
</Warning>

Token की समाप्ति से पहले हम `session` को फिर से कॉल करते हैं, ताकि पूरे दिन खुला छोड़ा गया पेज
भी काम करता रहे। जो कॉल reject होती है, या कोई token नहीं लौटाती, उसका दो बार और पुनः प्रयास किया
जाता है — 0.5s बाद, फिर 1s बाद — इससे पहले कि हम हार मानकर `error` emit करें। यानी एक refresh की
लागत आपके endpoint पर अधिकतम तीन कॉल है।

| Option | डिफ़ॉल्ट | करता है |
| - | - | - |
| `session` | — | **आवश्यक।** एक connect-mode लिंक सेशन लौटाता है। |
| `appearance` | हमारे डिफ़ॉल्ट | Design tokens, हर फ़्रेम के अंदर लागू। देखें [Appearance](#appearance)। |
| `css` | कोई नहीं | एक CSS स्ट्रिंग जो हर फ़्रेम के अंदर लागू होती है। देखें [कस्टम CSS](#custom-css)। |
| `maxHeight` | `600` | फ़्रेम कितना ऊँचा हो सकता है, इसके बाद वह अंदर ही scroll करता है। |

<h2 id="mount-a-slot">
  एक slot mount करें
</h2>

प्रति slot एक `mount`। एक नेटवर्क माँगें, कई, या वह सब कुछ जो सेशन अनुमति देता है — granularity
आपकी है, इसलिए slot किसी मौजूदा टेबल की एक row भी हो सकता है या सब कुछ रखने वाला एक panel।

```javascript theme={"system"}
connect.mount("#instagram-slot", { network: "instagram" });
connect.mount("#some-slot", { networks: ["facebook", "tiktok", "x"] });
connect.mount("#everything");

const row = connect.mount(document.querySelector("#tall"), { maxHeight: 1200 });
row.unmount();
```

`mount` एक CSS selector या element लेता है, और `{ unmount, element }` लौटाता है। यदि target किसी
से match नहीं करता तो यह throw करता है — जो लगभग हमेशा ऐसा slot होता है जो अभी मौजूद नहीं है,
इसलिए अपने markup के document में आने के बाद mount करें।

हर mount एक iframe है। वह अपनी ऊँचाई हमें रिपोर्ट करता है और हम उसे उसके अनुसार resize करते हैं,
इसलिए हमारी सामग्री बदलने पर आपका लेआउट reflow होता है; `maxHeight` से आगे फ़्रेम आपके पेज से
बाहर निकलने के बजाय अंदर ही scroll करता है। **हमारा समर्थित सबसे संकीर्ण slot 300px है।**

Network keys Ayrshare की अपनी हैं, और alias spellings भी काम करती हैं: `instagram` और
`instagramapi` दोनों का अर्थ `instagramApi` है, और `x` का अर्थ `twitter`। जो key कोई नेटवर्क नहीं
है, वह कोई tile render नहीं करती।

<h2 id="your-own-button">
  आपका अपना बटन
</h2>

जो ग्राहक हमारे किसी फ़्रेम के बजाय अपना बटन इस्तेमाल करना पसंद करे, वह इसके बजाय `popup` कॉल
करता है। यह वही फ़्लो, उसी सेशन पर चलाता है, और उन्हीं handlers पर रिपोर्ट करता है।

<Warning>
  **इसे सीधे click handler के अंदर कॉल करें, इससे पहले कुछ भी await किए बिना।** ब्राउज़र पॉपअप की
  अनुमति केवल तब तक देता है जब तक वह आपके उपयोगकर्ता के क्लिक को process कर रहा होता है, और वह
  अनुमति किसी `await` के बाद नहीं बचती। वैसे भी कुछ await करने की ज़रूरत नहीं है — सेशन `init` पर
  बन चुका था।
</Warning>

```javascript theme={"system"}
linkedInButton.addEventListener("click", () => {
  connect.popup({ network: "linkedin" });
});
```

`popup` `{ close(), network }` लौटाता है, और **हमेशा** एक handle लौटाता है — ब्लॉक हुए पॉपअप के
बाद भी, जहाँ `close()` कुछ नहीं करता — ताकि आपके कोड को `close()` कॉल करने से पहले कभी null-check
न करना पड़े।

यहाँ हर नेटवर्क काम करता है, उन नेटवर्कों समेत जिन्हें फ़्रेम अपने ही panel के अंदर पूरा करता है:
Facebook अपना hand-off explainer पॉपअप में दिखाता है, Bluesky और X अपना credential फ़ॉर्म दिखाते
हैं, और LinkedIn, Pinterest, YouTube और Google Business नेटवर्क तक जाकर लौटते हैं।

यह उन तीन चीज़ों के लिए synchronously throw करता है जो programming errors हैं — कोई `network`
नहीं, destroy किया हुआ instance, या ऐसा सेशन जो अभी resolve नहीं हुआ है। ब्लॉक हुआ पॉपअप उनमें से
**नहीं** है: वह `reason: "popupBlocked"` के साथ `error` emit करता है, क्योंकि आपके उपयोगकर्ता ने
कुछ ग़लत नहीं किया।

एक समय में अधिकतम एक पॉपअप खुला रहता है। दूसरी कॉल पहले को बंद कर देती है और उस पर
`reason: "superseded"` के साथ `cancelled` रिपोर्ट करती है। हमारे किसी **फ़्रेम** द्वारा खोला गया
पॉपअप अलग चीज़ है और उसे कभी छुआ नहीं जाता, इसलिए आपका बटन किसी mounted slot के अंदर चल रहे
फ़्लो को रद्द नहीं कर सकता।

<Note>
  सेशन किसी नेटवर्क को लिंक कर सकता है या नहीं, यह **server** का जवाब है, script का नहीं। Bluesky
  तक सीमित सेशन से LinkedIn माँगने पर पॉपअप में एक इनकार render होता है और वह `error` के रूप में
  रिपोर्ट होता है। Script केवल यह जाँचती है कि कोई नेटवर्क बताया गया था या नहीं।
</Note>

## React

Script framework-free है, इसलिए React को हमसे कुछ ख़ास नहीं चाहिए — लेकिन उसके lifecycle की चार
बातें पहली ही बार में सही कर लेना उचित है।

Script को **एक बार** लोड करें, अपने component tree के बाहर। Next.js में वह आपके root layout में
`next/script` है; Vite या Create React App में वह `index.html` में एक टैग है। इसे प्रति component
लोड करना हर mount पर उसे फिर से चलाता है।

```jsx ConnectAccounts.jsx theme={"system"}
import { useEffect, useRef, useState } from "react";

export function ConnectAccounts() {
  const slot = useRef(null);
  const [linked, setLinked] = useState([]);

  useEffect(() => {
    // Inside the effect, so the ref is attached: mount() throws if its target
    // does not exist yet, which is exactly what happens if you call it during render.
    const connect = window.AyrshareConnect.init({
      session: () => fetch("/my-api/ayrshare-session").then(r => r.json()),
      appearance: { "--ayr-connect-accent": "#0B7A6C" },
    });

    connect.mount(slot.current, { networks: ["instagram", "tiktok", "x"] });

    const stop = connect.on("success", ({ network }) => {
      setLinked(current => [...current, network]);
    });
    // `success` is one of ten events the widget reports. See Events below for
    // the full list, including `state`, which replaces polling.

    // destroy() takes the frames, the message listener and the timers with it.
    // Without this, a route change leaves all three behind.
    return () => {
      stop();
      connect.destroy();
    };
  }, []);

  return <div ref={slot} />;
}
```

<Warning>
  **`destroy()` के बाद instance का पुन: उपयोग कभी न करें।** destroy किया हुआ instance destroy ही
  रहता है — उस पर `popup()` throw करता है, और `mount()` उसे वापस नहीं लाएगा। अगले effect run में
  नया instance बनाएँ, जो ऊपर का कोड यही करता है।
</Warning>

उस pattern के दो परिणाम हैं, और दोनों में से कोई bug नहीं है:

* **development में आपको session callback दो बार fire होता दिखेगा।** React का Strict Mode effects
  को mount → unmount → mount क्रम में चलाता है, इसलिए instance बनता है, destroy होता है और फिर
  बनता है। ऊपर का cleanup इसे सुरक्षित बनाता है; dev में इसकी लागत आपके backend पर एक अतिरिक्त
  कॉल है और production में शून्य।
* **effect की dependencies स्थिर रखें।** parent render से सीधे `mount` में पास किया गया array
  literal हर बार नया मान होता है, इसलिए उस पर निर्भर effect हर render पर विजेट को तोड़कर फिर से
  बनाता है। इसे memoize करें, या ऊपर की तरह स्थिर रखें।

## Events

`on` से subscribe करें, जो एक unsubscribe फ़ंक्शन लौटाता है। Handlers को event का payload और वह
mount मिलता है जिससे वह आया; `off(name, handler)` वही काम करता है जब आप handler को नाम देना
पसंद करें।

```javascript theme={"system"}
const stop = connect.on("success", ({ network, displayName }, mount) => {
  refreshRow(network, displayName, mount.element);
});
stop();

connect.on("error", ({ network, code, message }) => report(code, message));
connect.destroy(); // every frame, listener and timer
```

दस events। हर एक में `network` होता है, सिवाय `ready` के, और उस एक तरह के `error` के जो किसी
नेटवर्क के बारे में है ही नहीं — नीचे देखें [`error` के दो स्रोत हैं](#error-has-two-sources)।

| Event | कब fire होता है | साथ में यह भी |
| - | - | - |
| `ready` | कोई फ़्रेम mount होकर तैयार है | — |
| `click` | आपके उपयोगकर्ता ने किसी नेटवर्क पर क्लिक किया, **दोनों दिशाओं में** | `action`: `"connect"` या `"unlink"` |
| `started` | कोई link प्रयास चल रहा है | — |
| `selection` | आपका उपयोगकर्ता किसी picker या फ़ॉर्म तक पहुँचा | `step` |
| `success` | अकाउंट कनेक्टेड **और सेव** है | `displayName` (अज्ञात होने पर छोड़ा जाता है), `refId` |
| `unlinked` | कोई कनेक्टेड अकाउंट हटाया गया, और हटाना सेव है | — |
| `error` | प्रयास विफल हुआ | `code`, `message`, `reason` |
| `cancelled` | आपका उपयोगकर्ता पीछे हट गया | `reason` |
| `closed` | इस प्रयास का पॉपअप बंद हो गया है | — |
| `state` | किसी नेटवर्क का account state, mount पर और हर बदलाव पर | `state`, `since` |

**उनमें से चार अंत हैं** — `success`, `unlinked`, `error` और `cancelled` — और प्रति प्रयास ठीक
एक ही आता है। `closed` एक lifecycle सूचना है जो किसी अंत के बाद आती है, स्वयं अंत नहीं।

`click` किसी भी linking काम के शुरू होने से **पहले** fire होता है, इसलिए वह ऐसा क्लिक भी रिपोर्ट
करता है जिसे ब्लॉक हुआ पॉपअप या मृत सेशन आगे चलकर ठुकरा देता है। आपकी अपनी analytics के लिए
यही event है; `started` वह है जिसका अर्थ है कि प्रयास वाक़ई चल रहा है।

### Reasons

| Event | `reason` | अर्थ |
| - | - | - |
| `error` | `popupBlocked` | ब्राउज़र ने पॉपअप खोलने से इनकार कर दिया |
| `cancelled` | `popupClosed` | आपके उपयोगकर्ता ने विंडो हाथ से बंद कर दी |
| `cancelled` | `scopesDenied` | आपके उपयोगकर्ता ने नेटवर्क द्वारा माँगी गई अनुमति अस्वीकार कर दी |
| `cancelled` | `userCancelled` | आपका उपयोगकर्ता पीछे हट गया, या आपके कोड ने `handle.close()` कॉल किया |
| `cancelled` | `superseded` | दूसरी `popup()` कॉल ने इस प्रयास को प्रतिस्थापित कर दिया |

<h3 id="error-has-two-sources">
  `error` के दो स्रोत हैं
</h3>

उनमें से केवल एक link की विफलता है, और वे अलग-अलग फ़ील्ड ले जाते हैं।

* **Link error** में `network`, `code` और वह mount होता है जिससे वह आया।
* **Session error** — हम आपका सेशन बना या refresh नहीं कर पाए — में केवल `message` होता है,
  क्योंकि उस समय कुछ भी लिंक नहीं हो रहा था।

Defensive तरीक़े से destructure करें: दूसरी तरह पर `code` और `network` `undefined` होते हैं।

### `state` आपको polling से बचाता है

`state` किसी प्रयास की रिपोर्ट के बजाय data channel है। हर फ़्रेम mount होते समय प्रति नेटवर्क एक
emit करता है, जिसमें उस नेटवर्क का वर्तमान state और `since` timestamp होता है जबसे वह उसे धारण
किए है, और हर बार जब कोई state बदलता है तब एक और — **हमारी तरफ़ से शुरू होने वाले बदलावों समेत**,
जैसे किसी token का मरकर relink-required में बदल जाना। इसलिए आप बिना कुछ भी poll किए विजेट से
अपना पूरा UI चला सकते हैं।

मान वही enum हैं जो
[`GET /profiles` `include=state` के साथ](/docs/apis/profiles/get-profiles) लौटाता है: `linked`,
`unlinked`, `identityVerificationRequired`, `restricted`, `rateLimited`, `suspended`।

## अनलिंक करना

हमारे tiles लिंक के साथ-साथ अनलिंक भी करते हैं। आपका उपयोगकर्ता किसी कनेक्टेड नेटवर्क पर क्लिक
करता है, पुष्टि करता है, और अकाउंट हटा दिया जाता है:

1. `click`, `action: "unlink"` के साथ fire होता है।
2. हटाना सेव हो जाने पर `unlinked` fire होता है।

जो अनलिंक **विफल** होता है वह `error` रिपोर्ट करता है, और जिससे आपका उपयोगकर्ता confirm चरण पर
पीछे हट जाता है वह `cancelled`। कोई अलग unlink-failed event नहीं है।

<h2 id="appearance">
  Appearance
</h2>

किसी ग्राहक की stylesheet cross-origin फ़्रेम के अंदर नहीं पहुँच सकती, इसलिए styling ऐसे data के
रूप में जाती है जिसे हम उसके भीतर लागू करते हैं। `appearance` को `init` में CSS custom properties
के रूप में पास करें; जो हमें नहीं मिलती वह हर एक हमारा डिफ़ॉल्ट रखती है।

```javascript theme={"system"}
const connect = AyrshareConnect.init({
  session: getSession,
  appearance: {
    "--ayr-connect-font-family": "Inter, system-ui, sans-serif",
    "--ayr-connect-surface-bg": "#111318",
    "--ayr-connect-surface-fg": "#F2F3F7",
    "--ayr-connect-accent": "#0B7A6C",
    "--ayr-connect-accent-fg": "#FFFFFF",
    "--ayr-connect-radius": "12px",
  },
});
```

<Warning>
  **रंग जोड़ियों में सेट करें।** बिना foreground token के background token ही एकमात्र तरीक़ा है
  जिससे यह कुछ अपठनीय बना सकता है: `--ayr-connect-surface-bg` को अकेले गहरे मान पर सेट करें और
  हमारा डिफ़ॉल्ट `--ayr-connect-surface-fg` फिर भी गहरा navy रहेगा। दूसरा आधा आपके लिए कोई
  नहीं भाँप सकता।
</Warning>

बिल्कुल कोई `appearance` न होने पर, हर token अपना डिफ़ॉल्ट रखता है और फ़्रेम ऐसा दिखता है:

<div className="my-8">
  <Frame caption="डिफ़ॉल्ट tokens: सफ़ेद surfaces, गहरा navy टेक्स्ट, indigo accent, 8px radius।">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-default.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=62f9ce7e061c8bb58132960b9e270f4a" alt="Three Ayrshare Connect tiles with the default appearance" width="920" height="528" data-path="images/multiple-users/connect-widget-appearance-default.webp" />
  </Frame>
</div>

मुट्ठी भर tokens पास करें और वही फ़्रेम आपका palette अपना लेता है। यह उदाहरण surfaces को हल्का
नीला करता है, टेक्स्ट और accent को उसके अनुरूप गहरा करता है, और कोनों को थोड़ा और गोल:

```javascript theme={"system"}
const connect = AyrshareConnect.init({
  session: getSession,
  appearance: {
    "--ayr-connect-surface-bg": "#EFF6FF",
    "--ayr-connect-border-color": "#BFDBFE",
    "--ayr-connect-surface-fg": "#0F2A5F",
    "--ayr-connect-surface-fg-muted": "#4A6A9A",
    "--ayr-connect-accent": "#1D4ED8",
    "--ayr-connect-radius": "14px",
    "--ayr-connect-spacing": "10px",
  },
});
```

<div className="my-8">
  <Frame caption="ऊपर के tokens लागू होने के बाद वही तीन tiles।">
    <img src="https://mintcdn.com/ayrshare-docs/-2tpx3mFy9vWIObd/images/multiple-users/connect-widget-appearance-themed.webp?fit=max&auto=format&n=-2tpx3mFy9vWIObd&q=85&s=686d1ad900628a6d4c75dc242f1ab38c" alt="Three Ayrshare Connect tiles restyled with pale blue surfaces and a deeper blue accent" width="920" height="574" data-path="images/multiple-users/connect-widget-appearance-themed.webp" />
  </Frame>
</div>

**एक ही palette है और कोई light/dark preset नहीं।** कुछ भी `prefers-color-scheme` पर आधारित नहीं
है, जानबूझकर — आपके पेज की थीम आपके उपयोगकर्ता के operating system से मेल न खाती हो सकती है, और
कोई media query आपके चुने रंगों को चुपचाप पछाड़ देती। Dark डैशबोर्ड को dark मान देकर थीम किया
जाता है।

यह token सूची एक समर्थित contract है जिसे हम versions के आर-पार बनाए रखते हैं।

### Tokens

| Token | डिफ़ॉल्ट |
| - | - |
| `--ayr-connect-font-family` | `Inter, "Segoe UI", system-ui, -apple-system, sans-serif` |
| `--ayr-connect-font-size` | `16px` |
| `--ayr-connect-font-size-sm` | `12px` |
| `--ayr-connect-label-font-weight` | `600` |
| `--ayr-connect-status-font-size` | `12px` |
| `--ayr-connect-status-font-weight` | `600` |
| `--ayr-connect-surface-bg` | `#FFFFFF` |
| `--ayr-connect-surface-bg-hover` | `#F7F7F7` |
| `--ayr-connect-surface-fg` | `#010629` |
| `--ayr-connect-surface-fg-muted` | `#56596F` |
| `--ayr-connect-border-color` | `#DDDEE2` |
| `--ayr-connect-border-width` | `1px` |
| `--ayr-connect-radius` | `8px` |
| `--ayr-connect-spacing` | `8px` |
| `--ayr-connect-accent` | `#4553EE` |
| `--ayr-connect-accent-fg` | `#FFFFFF` |
| `--ayr-connect-focus-ring-color` | `#4553EE` |
| `--ayr-connect-focus-ring-width` | `2px` |
| `--ayr-connect-disabled-fg` | `#6E7185` |
| `--ayr-connect-status-radius` | `4px` |
| `--ayr-connect-status-success-bg` | `#EBFFF8` |
| `--ayr-connect-status-success-fg` | `#237C5C` |
| `--ayr-connect-status-warning-bg` | `#FFF7EF` |
| `--ayr-connect-status-warning-fg` | `#702E00` |
| `--ayr-connect-status-critical-bg` | `#FFE5E1` |
| `--ayr-connect-status-critical-fg` | `#AD1902` |
| `--ayr-connect-status-info-bg` | `#ECF9FF` |
| `--ayr-connect-status-info-fg` | `#1F6686` |
| `--ayr-connect-callout-bg` | `#FFF7EF` |
| `--ayr-connect-callout-fg` | `#702E00` |
| `--ayr-connect-partner-name` | `""` |
| `--ayr-connect-icon-size` | `32px` |
| `--ayr-connect-avatar-size` | `40px` |

जो मान अपने token के लिए वैध CSS नहीं है, वह लागू होने के बजाय **आपके console में चेतावनी के साथ
नज़रअंदाज़** किया जाता है। यह जितना लगता है उससे अधिक मायने रखता है: `--ayr-connect-spacing` के
लिए unit-रहित `8` एक बिल्कुल मासूम स्ट्रिंग है जो उसे पढ़ने वाली हर गणना को अमान्य कर देती और
लेआउट ढहा देती, बिना कहीं किसी error के। लंबाइयों को unit दें।

### Hand-Off स्क्रीन पर आपका अपना नाम

आपके उपयोगकर्ता को किसी नेटवर्क को सौंपने से पहले, हम एक छोटी स्क्रीन दिखाते हैं जिस पर लिखा होता
है कि वे किसके माध्यम से कनेक्ट कर रहे हैं। दो hooks इसे आपका अपना बनाने देते हैं, और दोनों
version-stable हैं।

`--ayr-connect-partner-name` label सेट करता है। यह एकमात्र token है जिसका मान **टेक्स्ट** है,
इसलिए इसे CSS स्ट्रिंग के रूप में quote करना होगा — बिना quote का मान अमान्य है और कुछ भी render
नहीं करता:

```javascript theme={"system"}
AyrshareConnect.init({
  session: getSession,
  appearance: { "--ayr-connect-partner-name": "'Acme Social'" },
  css: "[data-ayr-connect-partner-mark]::after { background-image: url('https://cdn.example.com/mark.svg') }",
});
```

लोगो कोई token नहीं है। हम mark को positioned, sized element के रूप में भेजते हैं और आप उसे ऊपर
की तरह `css` के माध्यम से `background-image` से भरते हैं — ऐसा token जो हमारे document के अंदर से
कोई image fetch कर सके, हम स्वीकार नहीं करते, इसलिए request आपके लिखे किसी rule से आती है, आपके
हमें दिए किसी मान से नहीं।

<Note>
  `[data-ayr-connect-partner-mark]` और `[data-ayr-connect-partner-name]` नीचे दी गई custom-CSS
  चेतावनी के **अपवाद** हैं: ये दोनों selectors contract का हिस्सा हैं और हम इन्हें versions के
  आर-पार बनाए रखते हैं।
</Note>

<h3 id="custom-css">
  कस्टम CSS
</h3>

`css` एक ऐसी स्ट्रिंग लेता है जो हर फ़्रेम के अंदर लागू होती है, उन मामलों के लिए जिन्हें tokens
कवर नहीं करते।

```javascript theme={"system"}
AyrshareConnect.init({ session: getSession, css: "button { letter-spacing: 0.01em }" });
```

<Warning>
  **कस्टम CSS versions के आर-पार समर्थित नहीं है।** इसके selectors हमारे आंतरिक markup को target
  करते हैं, जो releases के बीच बदलता है — जो rule आज काम करता है वह किसी भी अपडेट के बाद चुपचाप
  match करना बंद कर सकता है। ऊपर का token contract वह हिस्सा है जिसे हम बनाए रखते हैं। यदि आप
  कस्टम CSS पर निर्भर हैं तो कोई version pin करें।
</Warning>

पॉपअप instance के `appearance` और `css` को बिल्कुल फ़्रेमों की तरह अपनाते हैं, ताकि आपका
उपयोगकर्ता किसी फ़्लो के बीच में हमारे neutral डिफ़ॉल्ट प्रकट होते न देखे।

## Ship करने से पहले जानने योग्य

<AccordionGroup>
  <Accordion title="जिस पॉपअप का सेशन समाप्त हो गया है वह error नहीं, cancelled रिपोर्ट करता है">
    `popup()` से खोले गए पॉपअप को यह नहीं बताया जा सकता कि token क्यों ठुकराया गया — ऐसा कहने के
    लिए उसे ऐसे origin पर भरोसा करना होगा जिसे उसने अभी validate नहीं किया है, जिसकी अनुमति हमारा
    security model नहीं देता। इसलिए मृत token ले जाने वाला पॉपअप बंद हो जाता है और `error` के बजाय
    `reason: "popupClosed"` के साथ `cancelled` के रूप में सामने आता है।

    व्यवहार में यह दुर्लभ है: silent refresh से पहले खोला गया पॉपअप काम करता रहता है, क्योंकि उसने
    खुलते समय अपना token validate कर लिया था। यदि आपको अस्पष्टीकृत `popupClosed` परिणाम दिखें, तो
    जाँचें कि आपका `session` endpoint ताज़ा सेशन लौटा रहा है।
  </Accordion>

  <Accordion title="प्रति instance एक सेशन, प्रति mount एक नहीं">
    चौदह mounts एक token साझा करते हैं और आपके backend पर एक कॉल की लागत लगाते हैं, चौदह नहीं। यदि
    आप अलग-अलग scope वाले slots चाहते हैं — उनमें से कुछ पर अलग `allowedSocial` — तो किसी mount से
    उसे संकीर्ण करने की अपेक्षा करने के बजाय अपने सेशन के साथ दूसरा `init` चलाएँ।
  </Accordion>

  <Accordion title="फ़्रेम केवल आपके घोषित origin पर render होते हैं">
    हर सेशन वह `origin` ले जाता है जिस पर आपका पेज चलता है, और फ़्रेम कुछ भी render करने से पहले
    उसे embed करने वाले पेज की तुलना उस मान से करता है। कहीं और embed किया गया फ़्रेम खाली रहता है
    और कोई events नहीं भेजता। register करने के लिए कोई allowlist नहीं है और configure करने के लिए
    कुछ नहीं — सेशन बनाते समय सही `origin` भेजें।
  </Accordion>

  <Accordion title="ऊँचाई आपके लिए संभाली जाती है, और वह कोई event नहीं है">
    फ़्रेम अपनी ऊँचाई script को रिपोर्ट करते हैं, और script उन्हें resize करती है। आपका लेआउट बस
    reflow होता है। subscribe करने के लिए कोई resize event नहीं है, और आपकी तरफ़ मापने के लिए कुछ
    नहीं।
  </Accordion>
</AccordionGroup>

## आवश्यकताएँ

<ul className="custom-bullets">
  <li>
    **[Max Pack](/docs/additional/maxpack)।** विजेट सेशन एक connect-mode सेशन है, और Max Pack के बिना
    एक बनाने पर `code: 504` लौटता है। यदि आपको बिना Max Pack वाले अकाउंट पर connect mode सक्षम
    कराना हो तो support से संपर्क करें।
  </li>

  <li>
    हर सेशन पर एक **`origin`** — ठीक वही origin जिस पर आपका पेज चलता है। इसे छोड़ने पर
    `code: 505` लौटता है; ऐसा मान जो `https` origin, custom scheme, या `http://localhost` नहीं
    है, `code: 506` लौटाता है।
  </li>

  <li>
    सेशन पर **कोई `network` नहीं**। वह पैरामीटर सेशन को इसके बजाय
    [डायरेक्ट मोड](/docs/multiple-users/connect-direct-mode) बना देता है, और direct-mode सेशन का URL
    वह नहीं है जिसकी script अपेक्षा करती है।
  </li>
</ul>

ऊपर का हर code
[Link Session त्रुटियाँ](/docs/errors/errors-ayrshare#link-session-errors) संदर्भ में है, API द्वारा
लौटाए जाने वाले सटीक message और उसके समाधान के साथ।


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.