> ## 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.

# Crear una Link Session

> Crea una URL de vinculación social para un user profile, sin enviar una clave privada.

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>;
};

export const HeaderAPI = ({noProfileKey, profileKeyRequired}) => <>
    <ParamField header="Authorization" type="string" required>
      <a href="/docs/apis/overview#authorization">API Key</a> of the Primary Profile.
      <br />
      <br />
      Format: <code>Authorization: Bearer API_KEY</code>
    </ParamField>
    {!noProfileKey && (profileKeyRequired ? <ParamField header="Profile-Key" type="string" required>
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField> : <ParamField header="Profile-Key" type="string">
          <a href="/docs/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField>)}
  </>;

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

Crea una URL de vinculación social para un User Profile. Envía la `url` devuelta a tu usuario,
y este la abre para conectar sus cuentas sociales.

Esta es la forma recomendada de crear una URL de vinculación. Solo necesita tu API Key y una
`Profile-Key`: no hay ninguna clave privada que enviar ni nada que firmar. A diferencia de una
URL de vinculación creada antes, una link session queda almacenada, así que puedes comprobar si
se ha usado y revocarla antes de que caduque.

La `url` devuelta inicia la sesión de tu usuario en su perfil, así que trátala como una
contraseña y envía cada una a un solo usuario. Consulta
[Enviar la URL de vinculación](/docs/apis/profiles/social-linking-overview#sending-the-linking-url).

<Note>
  La URL es válida durante **5 minutos** por defecto. Usa `expiresIn` para establecer una
  ventana distinta, de hasta 2880 minutos (48 horas).
</Note>

<Info>
  [Generar una URL de vinculación](/docs/apis/profiles/generate-jwt) realiza la misma operación y
  sigue funcionando sin cambios. Acepta los parámetros heredados `privateKey`, `base64` y
  `verify` y los ignora. `domain` no se ignora en ninguno de los dos endpoints: sigue siendo
  opcional y se sigue validando. Las nuevas integraciones deberían usar este endpoint.

  Una diferencia en la respuesta: `generateJWT` devuelve un `token` de nivel superior por
  retrocompatibilidad, y este endpoint no devuelve uno **junto a** una `url` — el token de una
  `url` vive dentro de ella. Si estás migrando y tu código lee `token`, lee `url` en su lugar.
  (El [modo connect](#connect-mode) para el widget incrustado es la única forma que devuelve un
  `token` suelto, porque no devuelve ninguna URL en la que el token pueda vivir.)
</Info>

## Parámetros de cabecera

<HeaderAPI profileKeyRequired={true} />

<Note>
  La `Profile-Key` es una cabecera en este endpoint: no hay ningún parámetro `profileKey` en el
  cuerpo. Si falta, obtienes `code: 188`, cuyo mensaje enumera `privateKey`, `profileKey` y
  otros nombres de campos heredados porque se comparte con
  [Generar una URL de vinculación](/docs/apis/profiles/generate-jwt). Interprétalo como «falta la
  cabecera Profile-Key o es incorrecta»; ninguno de los otros nombres que aparecen es un
  parámetro de este endpoint.
</Note>

<ParamField header="X-Twitter-OAuth1-Api-Key" type="string">
  Tu X API Key (Consumer Key) del X Developer Portal. Cuando se proporciona, la URL de
  vinculación usará tu propia X Developer App para la vinculación mediante OAuth.
</ParamField>

<ParamField header="X-Twitter-OAuth1-Api-Secret" type="string">
  Tu X API Secret (Consumer Secret) del X Developer Portal. Obligatorio cuando se proporciona
  `X-Twitter-OAuth1-Api-Key`.
</ParamField>

<h2 id="body-parameters">
  Parámetros del cuerpo
</h2>

<ParamField body="mode" type="string" default="grid">
  Qué superficie de vinculación controla esta sesión.

  * `grid` — la página de vinculación alojada, que muestra todas las redes que permites. Es el
    valor por defecto, así que una solicitud que omite `mode` crea una de estas.
  * `connect` — una red cada vez, abierta desde tu propio dashboard. Consulta
    [Modo connect](#connect-mode) más abajo y el [modo directo](/docs/multiple-users/connect-direct-mode).

  Nunca se infiere: pasar `origin` o `network` no te pone en modo connect, así que una sesión grid
  no puede convertirse en una con requisitos por accidente. Cualquier otro valor devuelve
  `code: 188` con `details` nombrando los dos.
</ParamField>

<ParamField body="expiresIn" type="number" default={5}>
  Duración del enlace en minutos. Rango: de 1 a 2880 minutos.

  Requiere el Max Pack.

  Consulta [Caducidad del enlace](/docs/apis/profiles/social-linking-overview#jwt-expires-in) para más información.
</ParamField>

<ParamField body="logout" type="boolean" default={false}>
  Cierra automáticamente la sesión actual. No se recomienda en producción, ya que afecta al
  rendimiento.

  Consulta [Cierre de sesión automático de una sesión de perfil](/docs/multiple-users/api-integration-business#automatic-logout-of-a-profile-session).
</ParamField>

<ParamField body="redirect" type="string">
  Una URL a la que redirigir cuando se haga clic en el botón "Done" (Hecho) o en la imagen del
  logo. Añade el parámetro de consulta `origin=true` para redirigir la ventana que abrió el
  enlace.
</ParamField>

<ParamField body="allowedSocial" type="array">
  Las redes sociales que se mostrarán en la página de vinculación. Sobrescribe las redes
  configuradas en la página [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access).

  ```json Only display Facebook, X/Twitter, LinkedIn, and TikTok theme={"system"}
  {
    "allowedSocial": ["facebook", "twitter", "linkedin", "tiktok"]
  }
  ```
</ParamField>

<ParamField body="network" type="string">
  Solo en modo connect. La única red social que conecta esta sesión, que es lo que la convierte en
  una sesión en **modo directo**. Omítelo para una sesión que tu propio dashboard controla a través
  de varias redes.

  Una de `bluesky`, `facebook`, `gmb`, `instagram`, `instagramApi`, `linkedin`, `pinterest`,
  `reddit`, `snapchat`, `telegram`, `threads`, `tiktok`, `twitter`, `whatsapp`, `x`, `youtube`.
  Cualquier otra cosa devuelve `code: 508` — incluido `fbg`, que aquí no es un destino de
  vinculación.

  No puede combinarse con `allowedSocial` (`code: 507`): una sesión de una sola red ya es su propia
  allowlist. Una red que tu cuenta no ha habilitado devuelve `code: 509`, que es una respuesta
  distinta de 508 porque se puede arreglar en tu página
  [Social Networks](/docs/multiple-users/manage-user-profiles#set-social-networks-access).

  En modo grid se ignora.
</ParamField>

<ParamField body="instagramLinkMethod" type="string">
  Sobrescribe el flujo de vinculación de Instagram que se usa para este enlace. Valores válidos:

  * `instagram`: Instagram Login directo, no requiere Facebook Page.
  * `facebook`: vincula Instagram a través de una Facebook Page conectada.

  Cuando se omite, la página de vinculación utiliza tu ajuste global de cuenta
  [Instagram Login](/docs/multiple-users/manage-user-profiles#instagram-login).
</ParamField>

<ParamField body="origin" type="string">
  El origen exacto de la página que abrió la ventana de vinculación, para que se le pueda avisar
  cuando la vinculación termine.

  Cuando se establece, la página de vinculación envía eventos a ese origen con `window.postMessage`
  a medida que tu usuario conecta cada cuenta, y tu página puede reaccionar sin hacer polling. Los
  eventos solo se envían a este valor exacto, así que debe coincidir con el origen de tu página
  carácter por carácter, incluidos el esquema y cualquier puerto.

  Se aceptan tres formas: un origen `https` (`https://app.example.com`),
  `http://localhost:3000` para desarrollo local, y un esquema personalizado nativo
  (`myapp://connected`). Cualquier otra cosa — un origen `http://` plano que no sea localhost, o
  algo que directamente no es un origen — se ignora en los enlaces que crea este endpoint: el
  enlace sigue funcionando, simplemente no envía eventos. Es opcional, así que omitirlo tampoco es
  un error.

  De las tres, solo las dos primeras reciben eventos. Un esquema personalizado es un destino de
  retorno para una app móvil y **no puede recibirlos**, porque no hay ventana de navegador a la que
  enviarlos; las apps nativas hacen polling de
  [Obtener una Link Session](/docs/apis/profiles/get-link-session) en su lugar.

  Consulta [Eventos de finalización de vinculación](/docs/multiple-users/link-completion-events).

  **En modo connect `origin` es obligatorio, y se comprueba.** La tolerancia de arriba es
  comportamiento del modo grid. Con `mode: "connect"`, omitirlo devuelve `code: 505` y un valor que
  no sea una de las tres formas aceptadas devuelve `code: 506`, cuyos `details` repiten la forma que
  enviaste.
</ParamField>

<ParamField body="domain" type="string">
  Opcional. Tu dominio de vinculación, cuando tu cuenta tiene más de uno. Si se omite, se usa
  el dominio propio de tu cuenta. Un dominio no registrado en tu cuenta se rechaza.
</ParamField>

<ParamField body="email" type="object">
  Envía un correo de Connect Accounts con el enlace, para que tu usuario pueda llegar
  directamente a su página de vinculación. Requiere una dirección `to`.

  Requiere el Max Pack. La respuesta informa del resultado en `emailSent`. Si el correo no se puede enviar, igualmente recibes el enlace: la respuesta incluye `emailSent: false` y un objeto `emailError`, para que puedas enviar el enlace a tu usuario de otra forma.

  Consulta [Connect Accounts Email](/docs/apis/profiles/social-linking-overview#connect-accounts-email).
</ParamField>

<h2 id="connect-mode">
  Modo connect
</h2>

`mode: "connect"` crea una sesión para una superficie de vinculación que alojas tú mismo, en lugar
de para la página de vinculación alojada. Cuál de las dos formas connect obtienes depende de una
cosa — si pasas `network`:

| Envías | Recibes de vuelta | Qué haces con ello |
| - | - | - |
| `mode: "connect"` y una `network` | una `url` que apunta a una página de conexión de una sola red | ábrela en una ventana emergente — esto es el [modo directo](/docs/multiple-users/connect-direct-mode) |
| `mode: "connect"` y ninguna `network` | un `token`, y ninguna URL | entrégaselo a tu propio front end |

<Note>
  **La respuesta lleva el secreto exactamente una vez.** Una respuesta tiene o una `url` o un
  `token`, nunca ambos y nunca dos URL. El token de una sesión en modo directo vive dentro de la
  `url`, igual que en modo grid; una sesión sin URL que lo lleve devuelve el `token` suelto en su
  lugar. Todo lo demás es igual en los tres modos: `sessionId`, `expiresAt`, `emailSent` y `title`
  cuando el User Profile tiene uno.
</Note>

### Qué requiere el modo connect

Ninguno de estos dos es un campo del cuerpo por sí mismo — el primero es una prestación de la cuenta
y el segundo es el parámetro [`origin`](#body-parameters) de arriba, que el modo connect hace
obligatorio.

**El [Max Pack](/docs/additional/maxpack).** Sin él, la llamada devuelve `code: 504`, comprobado antes
que los parámetros de modo connect, así que corregir `origin` o `network` no cambiará la respuesta.
Contacta con soporte si necesitas habilitar el modo connect en una cuenta sin el Max Pack.

**Un `origin`, en cada sesión.** No hay allowlist ni paso de registro — lo envías en cada llamada y
se almacena en la sesión, así que un entorno nuevo no necesita ninguna configuración en nuestro
lado. Se aceptan tres formas:

* un origen `https` — `https://app.example.com`
* un esquema personalizado nativo — `myapp://connected`
* `http://localhost` o `http://localhost:3000`, para desarrollo local

Solo el origen: sin ruta, consulta ni fragmento, y sin credenciales dentro. Omitirlo devuelve
`code: 505`, y cualquier cosa que no sea una de las tres formas devuelve `code: 506`.

<Warning>
  `email` no puede usarse con una sesión que no tiene `network`, porque no hay ningún enlace que
  poner en el correo — esa forma devuelve un token para tu propio front end. La llamada devuelve
  `code: 510`. Añade una `network` para una sesión en modo directo, que sí tiene URL, u omite
  `email`.
</Warning>

Todos los códigos nombrados arriba están en la referencia de
[Errores de link session](/docs/errors/errors-ayrshare#link-session-errors).

<RequestExample>
  ```bash cURL theme={"system"}
  curl \
  -H "Authorization: Bearer API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Profile-Key: PROFILE_KEY' \
  -d '{"expiresIn": 60}' \
  -X POST https://api.ayrshare.com/api/profiles/link-sessions
  ```

  ```javascript JavaScript theme={"system"}
  const API_KEY = "API_KEY";
  const PROFILE_KEY = "PROFILE_KEY";

  fetch("https://api.ayrshare.com/api/profiles/link-sessions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${API_KEY}`,
      "Profile-Key": PROFILE_KEY,
    },
    body: JSON.stringify({ expiresIn: 60 }),
  })
    .then((res) => res.json())
    .then((json) => console.log(json))
    .catch(console.error);
  ```

  ```python Python theme={"system"}
  import requests

  payload = {'expiresIn': 60}
  headers = {'Content-Type': 'application/json',
          'Authorization': 'Bearer API_KEY',
          'Profile-Key': 'PROFILE_KEY'}

  response = requests.post('https://api.ayrshare.com/api/profiles/link-sessions',
                           json=payload, headers=headers)
  print(response.json())
  ```

  ```php PHP theme={"system"}
  <?php
  require 'vendor/autoload.php';    // Composer auto-loader using Guzzle. See .../guzzlephp.org/en/stable/overview.html

  $client = new GuzzleHttp\Client();
  $res = $client->request(
      'POST',
      'https://api.ayrshare.com/api/profiles/link-sessions',
      [
          'headers' => [
              'Content-Type'  => 'application/json',
              'Authorization' => 'Bearer API_KEY',
              'Profile-Key'   => 'PROFILE_KEY'
          ],
          'json' => [
              'expiresIn' => 60,
          ]
      ]
  );

  echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT);
  ```

  ```csharp C# theme={"system"}
  using System;
  using System.Net.Http;
  using System.Text;
  using System.Threading.Tasks;
  using Newtonsoft.Json;

  namespace CreateLinkSession_csharp
  {
    class CreateLinkSession
    {
        static async Task Main(string[] args)
        {
            string API_KEY = "API_KEY";
            string PROFILE_KEY = "PROFILE_KEY";
            string url = "https://api.ayrshare.com/api/profiles/link-sessions";

            try
            {
                using (var client = new HttpClient())
                {
                    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY);
                    client.DefaultRequestHeaders.Add("Profile-Key", PROFILE_KEY);

                    var sendData = new { expiresIn = 60 };
                    string jsonData = JsonConvert.SerializeObject(sendData);
                    var content = new StringContent(jsonData, Encoding.UTF8, "application/json");

                    HttpResponseMessage response = await client.PostAsync(url, content);
                    response.EnsureSuccessStatusCode();

                    string responseBody = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(responseBody);
                }
            }
            catch (HttpRequestException e)
            {
                Console.WriteLine($"HTTP request error: {e.Message}");
            }
        }
    }
  }
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",  // Identifier for this link. Use it with Get and Revoke a Link Session.
      "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN",  // Send this to your user exactly as returned. The token exists only in here.
      "expiresAt": "2026-09-02T08:03:26.838Z",  // When the link stops working, as an ISO 8601 timestamp.
      "emailSent": false,  // Whether the connect-accounts email was sent. false with no emailError means no email was requested.
      "title": "Acme Client"  // The User Profile's title. Omitted when the profile has none.
  }
  ```

  ```json 200: Email Failed to Send theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
      "url": "https://profile.ayrshare.com?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu&domain=YOUR_DOMAIN",  // Still valid. Send it to your user yourself.
      "expiresAt": "2026-09-02T08:03:26.838Z",
      "emailSent": false,
      "emailError": {  // Only present when an email was requested and could not be sent.
          "code": 333,
          "message": "Email failed to send. Please confirm a valid email was provided."
      },
      "title": "Acme Client"
  }
  ```

  ```json 200: Direct Mode (mode: "connect" with a network) theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
      "url": "https://profile.ayrshare.com/connect?session=ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu",  // Open this in a popup. One network, no domain parameter.
      "expiresAt": "2026-09-02T08:03:26.838Z",
      "emailSent": false,
      "title": "Acme Client"
  }
  ```

  ```json 200: Connect Mode Without a Network theme={"system"}
  {
      "status": "success",
      "sessionId": "c7a2434e72e91bde27579efde0fd6dd0b74ceee29a471fd368407f273708c2e8",
      "token": "ayr_ls_kJ8mQ2vX9pLnR4tYwZ6aBcD1eFgH3iJkLmN0oPqRsTu",  // No url: this shape returns the bare token instead. Treat it like a password.
      "expiresAt": "2026-09-02T08:03:26.838Z",
      "emailSent": false,  // Always false here - email needs a link to send, so it returns code: 510.
      "title": "Acme Client"
  }
  ```

  ```json 401: Connect Mode Requires the Max Pack theme={"system"}
  {
    "action": "link session",
    "status": "error",
    "code": 504,
    "message": "Connect mode requires the Max Pack. Activate it on your Account page: https://app.ayrshare.com/account"
  }
  ```

  ```json 400: Missing Profile-Key Header theme={"system"}
  {
    "action": "JWT",
    "status": "error",
    "code": 188,
    "message": "Missing or incorrect privateKey, profileKey, domain, email 'to', or expiresIn fields."
  }
  ```

  ```json 400: Domain Not Registered to Your Account theme={"system"}
  {
    "action": "JWT",
    "status": "error",
    "code": 189,
    "message": "Error generating JWT. Check the sent parameters.",
    "details": "Missing or incorrect domain."
  }
  ```

  ```json 403: expiresIn Requires the Max Pack theme={"system"}
  {
    "action": "JWT",
    "status": "error",
    "code": 340,
    "message": "Max Pack required. Go to your dashboard to add the Max Pack."
  }
  ```
</ResponseExample>


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