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

# Supply Chain

> Retrieve supply chain relationships for a symbol (e.g. suppliers, customers, or related entities).

**Use Case:** Visualize or list a company's supply chain for fundamental and risk analysis.


export default function OAuthLoginButton({scopes = "user:information account:information order:execution order:information position:information market:information calendar:information options:information analytics:information market:supplemental", responseType = "token", buttonText = "Authenticate with OAuth", showStatus = true}) {
  const [isDarkMode, setIsDarkMode] = useState(false);
  useEffect(() => {
    const checkDarkMode = () => {
      const isDark = document.documentElement.classList.contains("dark") || document.documentElement.getAttribute("data-theme") === "dark" || document.body.classList.contains("dark") || document.body.getAttribute("data-theme") === "dark";
      setIsDarkMode(isDark);
    };
    checkDarkMode();
    const htmlObserver = new MutationObserver(checkDarkMode);
    const bodyObserver = new MutationObserver(checkDarkMode);
    htmlObserver.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class", "data-theme", "style"]
    });
    bodyObserver.observe(document.body, {
      attributes: true,
      attributeFilter: ["class", "data-theme", "style"]
    });
    const interval = setInterval(checkDarkMode, 1000);
    return () => {
      htmlObserver.disconnect();
      bodyObserver.disconnect();
      clearInterval(interval);
    };
  }, []);
  const styles = useMemo(() => ({
    statusContainer: {
      marginBottom: "1.5rem",
      padding: "0.875rem 1rem",
      backgroundColor: isDarkMode ? "rgba(11, 170, 94, 0.08)" : "rgba(11, 170, 94, 0.06)",
      border: isDarkMode ? "1px solid rgba(11, 170, 94, 0.25)" : "1px solid rgba(11, 170, 94, 0.25)",
      borderRadius: "10px"
    },
    statusContent: {
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      gap: "1rem",
      flexWrap: "wrap"
    },
    statusLeft: {
      display: "flex",
      alignItems: "center",
      gap: "1rem",
      flexWrap: "wrap"
    },
    statusBadge: {
      display: "flex",
      alignItems: "center",
      gap: "0.5rem"
    },
    checkmark: {
      color: isDarkMode ? "#0BAA5E" : "#0BAA5E",
      fontSize: "1.25rem",
      fontWeight: "bold"
    },
    statusText: {
      color: isDarkMode ? "#0BAA5E" : "#0BAA5E",
      fontWeight: "600",
      fontSize: "0.95rem"
    },
    expiryText: {
      color: isDarkMode ? "#a1a1aa" : "#71717a",
      fontSize: "0.8125rem"
    },
    logoutButton: {
      padding: "0.375rem 0.75rem",
      backgroundColor: "transparent",
      color: isDarkMode ? "#e4e4e7" : "#3f3f46",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.1)",
      borderRadius: "6px",
      cursor: "pointer",
      fontSize: "0.8125rem",
      fontWeight: "500",
      transition: "background-color 0.15s ease"
    },
    logoutButtonHover: {
      backgroundColor: isDarkMode ? "rgba(255, 255, 255, 0.05)" : "rgba(0, 0, 0, 0.04)"
    },
    buttonContainer: {
      marginBottom: "1.5rem",
      display: "flex",
      alignItems: "center",
      justifyContent: "space-between",
      gap: "1rem",
      padding: "0.875rem 1rem",
      backgroundColor: "transparent",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.08)",
      borderRadius: "10px"
    },
    loginButton: {
      display: "inline-flex",
      alignItems: "center",
      gap: "0.5rem",
      padding: "0.5rem 0.875rem",
      backgroundColor: "#0BAA5E",
      color: "white",
      border: "1px solid #0BAA5E",
      borderRadius: "8px",
      cursor: "pointer",
      fontSize: "0.8125rem",
      fontWeight: "500",
      lineHeight: "1.25rem",
      transition: "background-color 0.15s ease, border-color 0.15s ease",
      whiteSpace: "nowrap",
      flexShrink: 0
    },
    loginButtonHover: {
      backgroundColor: "#098F4E",
      borderColor: "#098F4E"
    },
    lockIcon: {
      width: "14px",
      height: "14px",
      flexShrink: 0
    },
    helpText: {
      margin: 0,
      fontSize: "0.8125rem",
      color: isDarkMode ? "#9ca3af" : "#6b7280",
      lineHeight: "1.5",
      flex: 1
    },
    modalDialog: {
      border: "none",
      backgroundColor: "transparent",
      padding: 0,
      margin: 0,
      maxWidth: "100%",
      maxHeight: "100%"
    },
    modalContent: {
      backgroundColor: isDarkMode ? "#0f0f10" : "#ffffff",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.08)" : "1px solid rgba(0, 0, 0, 0.08)",
      borderRadius: "12px",
      padding: "1.5rem",
      maxWidth: "500px",
      width: "100%",
      boxShadow: isDarkMode ? "0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.3)" : "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
    },
    modalHeader: {
      display: "flex",
      justifyContent: "space-between",
      alignItems: "center",
      marginBottom: "1.5rem"
    },
    modalTitle: {
      margin: 0,
      fontSize: "1.125rem",
      fontWeight: "600",
      color: isDarkMode ? "#fafafa" : "#0a0a0a"
    },
    closeButton: {
      background: "none",
      border: "none",
      fontSize: "1.5rem",
      lineHeight: 1,
      color: isDarkMode ? "#a1a1aa" : "#71717a",
      cursor: "pointer",
      padding: "0",
      width: "2rem",
      height: "2rem",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      borderRadius: "6px",
      transition: "color 0.15s ease, background-color 0.15s ease"
    },
    form: {
      display: "flex",
      flexDirection: "column",
      gap: "1rem"
    },
    formGroup: {
      display: "flex",
      flexDirection: "column",
      gap: "0.5rem"
    },
    label: {
      fontSize: "0.8125rem",
      fontWeight: "500",
      color: isDarkMode ? "#e4e4e7" : "#3f3f46"
    },
    input: {
      padding: "0.5rem 0.75rem",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.1)",
      borderRadius: "8px",
      fontSize: "0.875rem",
      color: isDarkMode ? "#fafafa" : "#0a0a0a",
      backgroundColor: isDarkMode ? "rgba(255, 255, 255, 0.03)" : "#ffffff",
      transition: "border-color 0.15s ease, box-shadow 0.15s ease",
      outline: "none"
    },
    inputHelp: {
      margin: 0,
      fontSize: "0.75rem",
      color: isDarkMode ? "#a1a1aa" : "#71717a"
    },
    link: {
      color: "#0BAA5E",
      textDecoration: "none",
      fontWeight: "500"
    },
    errorBox: {
      padding: "0.75rem",
      backgroundColor: isDarkMode ? "rgba(220, 38, 38, 0.1)" : "rgba(220, 38, 38, 0.05)",
      border: isDarkMode ? "1px solid rgba(220, 38, 38, 0.3)" : "1px solid rgba(220, 38, 38, 0.2)",
      borderRadius: "8px",
      color: isDarkMode ? "#fca5a5" : "#dc2626",
      fontSize: "0.8125rem"
    },
    modalFooter: {
      display: "flex",
      gap: "0.5rem",
      justifyContent: "flex-end",
      marginTop: "0.5rem"
    },
    cancelButton: {
      padding: "0.5rem 0.875rem",
      backgroundColor: "transparent",
      color: isDarkMode ? "#e4e4e7" : "#3f3f46",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.1)",
      borderRadius: "8px",
      cursor: "pointer",
      fontSize: "0.8125rem",
      fontWeight: "500",
      lineHeight: "1.25rem",
      transition: "background-color 0.15s ease"
    },
    submitButton: {
      padding: "0.5rem 0.875rem",
      backgroundColor: "#0BAA5E",
      color: "white",
      border: "1px solid #0BAA5E",
      borderRadius: "8px",
      cursor: "pointer",
      fontSize: "0.8125rem",
      fontWeight: "500",
      lineHeight: "1.25rem",
      transition: "background-color 0.15s ease, border-color 0.15s ease"
    },
    submitButtonDisabled: {
      opacity: 0.6,
      cursor: "not-allowed"
    },
    redirectUrlContainer: {
      display: "flex",
      alignItems: "center",
      gap: "0.5rem",
      padding: "0.5rem 0.75rem",
      backgroundColor: isDarkMode ? "rgba(255, 255, 255, 0.03)" : "rgba(0, 0, 0, 0.03)",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.08)" : "1px solid rgba(0, 0, 0, 0.08)",
      borderRadius: "8px",
      fontSize: "0.8125rem",
      fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"
    },
    redirectUrlText: {
      flex: 1,
      color: isDarkMode ? "#a1a1aa" : "#71717a",
      overflow: "hidden",
      textOverflow: "ellipsis",
      whiteSpace: "nowrap"
    },
    copyButton: {
      padding: "0.25rem 0.625rem",
      backgroundColor: "transparent",
      color: isDarkMode ? "#e4e4e7" : "#3f3f46",
      border: isDarkMode ? "1px solid rgba(255, 255, 255, 0.1)" : "1px solid rgba(0, 0, 0, 0.1)",
      borderRadius: "6px",
      cursor: "pointer",
      fontSize: "0.75rem",
      fontWeight: "500",
      transition: "background-color 0.15s ease, border-color 0.15s ease",
      whiteSpace: "nowrap"
    },
    copyButtonHover: {
      backgroundColor: isDarkMode ? "rgba(255, 255, 255, 0.05)" : "rgba(0, 0, 0, 0.04)"
    },
    copyButtonCopied: {
      backgroundColor: "#0BAA5E",
      color: "white",
      border: "1px solid #0BAA5E"
    }
  }), [isDarkMode]);
  const [isModalOpen, setIsModalOpen] = useState(false);
  const [clientId, setClientId] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const [hasValidToken, setHasValidToken] = useState(false);
  const [tokenSource, setTokenSource] = useState(null);
  const [timeRemaining, setTimeRemaining] = useState(0);
  const [error, setError] = useState("");
  const [copied, setCopied] = useState(false);
  const redirectUrl = typeof window !== "undefined" ? window.location.origin + "/api-reference" : "";
  useEffect(() => {
    updateTokenState();
    if (window.AriesOAuth) {
      const lastClientId = window.AriesOAuth.getLastClientId();
      setClientId(lastClientId);
    }
    const handleTokenUpdate = () => {
      updateTokenState();
    };
    const handleOAuthError = event => {
      setError(event.detail.description || "Authentication failed");
      setIsLoading(false);
    };
    const handleOAuthSuccess = () => {
      setIsModalOpen(false);
      setIsLoading(false);
      setError("");
    };
    window.addEventListener("tokenStatusChanged", handleTokenUpdate);
    window.addEventListener("tokenUpdated", handleTokenUpdate);
    window.addEventListener("tokenCleared", handleTokenUpdate);
    window.addEventListener("oauthError", handleOAuthError);
    window.addEventListener("oauthSuccess", handleOAuthSuccess);
    const interval = setInterval(() => {
      if (hasValidToken) {
        updateTokenState();
      }
    }, 1000);
    return () => {
      window.removeEventListener("tokenStatusChanged", handleTokenUpdate);
      window.removeEventListener("tokenUpdated", handleTokenUpdate);
      window.removeEventListener("tokenCleared", handleTokenUpdate);
      window.removeEventListener("oauthError", handleOAuthError);
      window.removeEventListener("oauthSuccess", handleOAuthSuccess);
      clearInterval(interval);
    };
  }, [hasValidToken]);
  const updateTokenState = useCallback(() => {
    if (window.AriesOAuth) {
      const isValid = window.AriesOAuth.isAuthenticated();
      const remaining = window.AriesOAuth.getTimeRemaining();
      const knownExpiry = window.AriesOAuth.hasKnownExpiry ? window.AriesOAuth.hasKnownExpiry() : false;
      const source = window.AriesOAuth.getTokenSource ? window.AriesOAuth.getTokenSource() : null;
      setHasValidToken(isValid);
      setTokenSource(source);
      setTimeRemaining(knownExpiry && typeof remaining === "number" ? remaining : null);
    }
  }, []);
  const handleLogin = () => {
    setError("");
    setIsModalOpen(true);
  };
  const handleLogout = () => {
    if (window.AriesOAuth) {
      window.AriesOAuth.logout();
    }
    try {
      const cfg = window.AriesOAuth?.config;
      const tokenKey = cfg?.TOKEN_KEY ?? "aries_access_token";
      const expiryKey = cfg?.TOKEN_EXPIRY_KEY ?? "aries_token_timestamp";
      const sourceKey = cfg?.TOKEN_SOURCE_KEY ?? "aries_token_source";
      localStorage.removeItem(tokenKey);
      localStorage.removeItem(expiryKey);
      localStorage.removeItem(sourceKey);
    } catch (_) {}
    setHasValidToken(false);
    setTimeRemaining(null);
  };
  const handleOAuthRedirect = async e => {
    e.preventDefault();
    if (!clientId.trim()) {
      setError("Please enter your Client ID");
      return;
    }
    setIsLoading(true);
    try {
      if (window.AriesOAuth) {
        window.AriesOAuth.saveClientId(clientId.trim());
      }
      const returnPath = window.location.pathname + window.location.search + window.location.hash;
      sessionStorage.setItem("oauth_return_path", returnPath);
      const pkce = await window.AriesOAuth.generatePKCE();
      const authEndpoint = window.AriesOAuth?.config?.AUTH_ENDPOINT || "https://app.aries.com/oauth2/authorize";
      const authUrl = new URL(authEndpoint);
      authUrl.searchParams.set("client_id", clientId.trim());
      authUrl.searchParams.set("redirect_uri", redirectUrl);
      authUrl.searchParams.set("response_type", "code");
      authUrl.searchParams.set("code_challenge", pkce.challenge);
      authUrl.searchParams.set("code_challenge_method", "S256");
      authUrl.searchParams.set("scope", scopes);
      authUrl.searchParams.set("state", generateState());
      const fullAuthUrl = authUrl.toString();
      setTimeout(() => {
        window.location.href = fullAuthUrl;
      }, 100);
    } catch (error) {
      setError("Failed to initiate OAuth flow: " + error.message);
      setIsLoading(false);
    }
  };
  const generateState = () => {
    return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
  };
  const formatTimeRemaining = seconds => {
    const minutes = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${minutes}m ${secs}s`;
  };
  const copyRedirectUrl = () => {
    navigator.clipboard.writeText(redirectUrl).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }).catch(() => {});
  };
  const oauthDialogRef = useRef(null);
  const isLoadingRef = useRef(false);
  isLoadingRef.current = isLoading;
  const closeModal = useCallback(() => {
    if (!isLoading) {
      setIsModalOpen(false);
      setError("");
      setCopied(false);
    }
  }, [isLoading]);
  useEffect(() => {
    const el = oauthDialogRef.current;
    if (!el) return;
    if (isModalOpen) {
      if (!el.open) el.showModal();
    } else {
      if (el.open) el.close();
    }
  }, [isModalOpen]);
  useEffect(() => {
    const el = oauthDialogRef.current;
    if (!el) return;
    const onClose = () => {
      setIsModalOpen(false);
      setError("");
      setCopied(false);
    };
    const onCancel = e => {
      if (isLoadingRef.current) e.preventDefault();
    };
    el.addEventListener("close", onClose);
    el.addEventListener("cancel", onCancel);
    return () => {
      el.removeEventListener("close", onClose);
      el.removeEventListener("cancel", onCancel);
    };
  }, [hasValidToken, showStatus]);
  if (hasValidToken && showStatus && tokenSource === "oauth") {
    return <div style={styles.statusContainer}>
        <div style={styles.statusContent}>
          <div style={styles.statusLeft}>
            <div style={styles.statusBadge}>
              <span style={styles.checkmark}>✓</span>
              <span style={styles.statusText}>Authenticated</span>
            </div>
            {typeof timeRemaining === "number" ? <span style={styles.expiryText}>
                Expires in {formatTimeRemaining(timeRemaining)}
              </span> : null}
          </div>
          <button onClick={handleLogout} style={styles.logoutButton} onMouseEnter={e => e.target.style.backgroundColor = styles.logoutButtonHover.backgroundColor} onMouseLeave={e => e.target.style.backgroundColor = styles.logoutButton.backgroundColor}>
            Logout
          </button>
        </div>
      </div>;
  }
  const onDialogClick = e => {
    if (e.target === e.currentTarget) {
      closeModal();
    }
  };
  const modalMarkup = <dialog ref={oauthDialogRef} className="aries-oauth-dialog" data-oauth-theme={isDarkMode ? "dark" : "light"} style={styles.modalDialog} onClick={onDialogClick}>
      <div style={styles.modalContent} onClick={e => e.stopPropagation()}>
        <div style={styles.modalHeader}>
          <h3 style={styles.modalTitle}>OAuth Authentication</h3>
          <button type="button" onClick={closeModal} style={styles.closeButton} disabled={isLoading}>
            ×
          </button>
        </div>

        <form onSubmit={handleOAuthRedirect} style={styles.form}>
          <div style={styles.formGroup}>
            <label htmlFor="clientId" style={styles.label}>
              Client ID
            </label>
            <input id="clientId" type="text" value={clientId} onChange={e => setClientId(e.target.value)} placeholder="Enter your OAuth Client ID" style={styles.input} disabled={isLoading} />
            <p style={styles.inputHelp}>
              Get your Client ID from{" "}
              <a href="https://app.aries.com/client-center/api" target="_blank" rel="noopener noreferrer" style={styles.link}>
                Client Center / Manage Account → API
              </a>
            </p>
          </div>

          <div style={styles.formGroup}>
            <label style={styles.label}>Redirect URL</label>
            <div style={styles.redirectUrlContainer}>
              <span style={styles.redirectUrlText} title={redirectUrl}>
                {redirectUrl}
              </span>
              <button type="button" onClick={copyRedirectUrl} style={{
    ...styles.copyButton,
    ...copied ? styles.copyButtonCopied : {}
  }} onMouseEnter={e => {
    if (!copied) {
      e.currentTarget.style.backgroundColor = styles.copyButtonHover.backgroundColor;
    }
  }} onMouseLeave={e => {
    if (!copied) {
      e.currentTarget.style.backgroundColor = styles.copyButton.backgroundColor;
    }
  }}>
                {copied ? "✓ Copied" : "Copy"}
              </button>
            </div>
            <p style={styles.inputHelp}>
              Add this URL to your OAuth client's allowed redirect URIs
            </p>
          </div>

          {error && <div style={styles.errorBox}>{error}</div>}

          <div style={styles.modalFooter}>
            <button type="button" onClick={closeModal} style={styles.cancelButton} disabled={isLoading}>
              Cancel
            </button>
            <button type="submit" style={{
    ...styles.submitButton,
    ...isLoading ? styles.submitButtonDisabled : {}
  }} disabled={isLoading}>
              {isLoading ? "Redirecting..." : "Initiate Login"}
            </button>
          </div>
        </form>
      </div>
    </dialog>;
  return <>
      {}
      <div style={styles.buttonContainer}>
        <div style={styles.helpText}>
          <strong style={{
    color: isDarkMode ? "#e5e7eb" : "#374151"
  }}>
            Quick Auth:
          </strong>{" "}
          Authenticate once to auto-fill your Bearer token across all API
          endpoints
        </div>
        <button onClick={handleLogin} style={styles.loginButton} onMouseEnter={e => {
    e.currentTarget.style.backgroundColor = styles.loginButtonHover.backgroundColor;
    e.currentTarget.style.borderColor = styles.loginButtonHover.borderColor;
  }} onMouseLeave={e => {
    e.currentTarget.style.backgroundColor = styles.loginButton.backgroundColor;
    e.currentTarget.style.borderColor = styles.loginButton.borderColor;
  }}>
          <svg style={styles.lockIcon} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
          </svg>
          {buttonText}
        </button>
      </div>

      {modalMarkup}
    </>;
}

<OAuthLoginButton />

## Query parameters

| Parameter | Type   | Required | Description                      |
| --------- | ------ | -------- | -------------------------------- |
| `symbol`  | string | Yes      | Stock ticker symbol (e.g. AAPL). |

## Response

Success returns `200 OK` with a JSON object containing supply chain relationship data.

Errors: `400` when symbol is missing, `401` unauthorized, `404` when no data found, `500` server error.


## OpenAPI

````yaml openapi.json GET /v1/stock/supply-chain
openapi: 3.0.3
info:
  contact:
    email: dev@aries.com
    name: Aries Financial
  description: >-
    OpenAPI Specification for the Aries trading platform API.


    # Authentication


    Learn how to authenticate with the Aries API using OAuth2 and manage access
    tokens in your SDK.


    ## Overview


    The Aries API uses **OAuth2 with Bearer tokens** (JWT format) for
    authentication. All API requests require a valid access token in the
    `Authorization` header:


    ```

    Authorization: Bearer <access_token>

    ```


    ## Providing Client ID and Client Secret (SDK)


    When using the generated SDK, provide your **Client ID** and **Client
    Secret** when you create the API client (e.g. in the constructor or security
    options). The SDK will use these to obtain and refresh the access token
    internally; you do not need to manage tokens yourself.


    Obtain your OAuth2 credentials from the Aries platform (e.g. Client Center /
    Manage Account at https://app.aries.com):


    - **Client ID** – Your application identifier (pass to SDK client)

    - **Client Secret** – Your application secret key (pass to SDK client; use
    PKCE for public clients where secret cannot be stored)


    ## Authentication Flow


    ### 1. Authorization Code Flow


    For server-side or confidential clients:


    1. **Redirect the user** to the authorization URL to sign in and consent:
     - **URL:** `https://app.aries.com/oauth2/authorize`
     - **Query params:** `response_type=code`, `client_id`, `redirect_uri`, `scope`, `state`

    2. **Exchange the code for tokens** (after user is redirected back with
    `?code=.`):
     - **POST** `https://api.aries.com/v1/oauth2/token`
     - **Body:** `grant_type=authorization_code`, `code`, `redirect_uri`, `client_id`, `client_secret`
     - Response includes `access_token` and `refresh_token`

    3. **Call the API** with the access token: `Authorization: Bearer
    <access_token>`


    ### 2. PKCE Flow


    For SPAs and mobile apps (public clients that cannot store `client_secret`):


    1. Generate a **code_verifier** (random string) and **code_challenge** =
    BASE64URL(SHA256(code_verifier)).

    2. **Redirect the user** to `https://app.aries.com/oauth2/authorize` with
    `code_challenge`, `code_challenge_method=S256`, plus `client_id`,
    `redirect_uri`, `scope`, `state`.

    3. **Exchange the code** at POST `https://api.aries.com/v1/oauth2/token`
    with `grant_type=authorization_code`, `code`, `redirect_uri`, `client_id`,
    `code_verifier` (no client_secret).

    4. Use the returned `access_token` as Bearer.


    ### 3. MFA Verification


    If the user has MFA enabled, the authorize step may return `is_mfa: true`
    and a `next_step_auth_id`. Call **POST**
    `https://api.aries.com/v1/oauth2/authorize/mfa` with `next_step_auth_id` and
    `verification_code` (6-digit code). Then continue with **POST**
    `/v1/oauth2/authorize/confirm` to get the authorization code, and exchange
    it at `/v1/oauth2/token`.


    ## Token Management


    - **Refresh when expired:** POST `https://api.aries.com/v1/oauth2/token`
    with `grant_type=refresh_token`, `client_id`, `client_secret`,
    `refresh_token`.

    - **Using a Bearer token directly:** If you already have an access token,
    set the header `Authorization: Bearer <access_token>` on every request. The
    SDK can accept a pre-obtained token and use it until it expires.


    ## OAuth2 Scopes


    Request only the scopes your application needs. Available scopes:


    | Scope | Description |

    |-------|-------------|

    | `user:information` | View user profile and personal details |

    | `account:information` | View account balances, positions, and transaction
    history |

    | `order:execution` | Place, modify, and cancel orders |

    | `order:information` | View order history and status |

    | `position:information` | View current positions and holdings |

    | `market:information` | Access live and historical market data |

    | `calendar:information` | Access earnings, economic, and market schedule
    data |

    | `options:information` | Access options chains and expiration data |

    | `analytics:information` | View analytics, ratings, and market insights |

    | `market:supplemental` | News, company profiles, financials, filings, ETF
    data, technical analysis |


    Specify multiple scopes as a space-separated string, e.g.
    `account:information order:execution market:information`.


    ## Security Best Practices


    - **Store credentials securely** – Use environment variables or a secrets
    manager for `client_id` and `client_secret`. Never hardcode them.

    - **Handle token expiration** – Check for 401 responses and refresh the
    token using the refresh_token, then retry the request.

    - **Use HTTPS** – All authorization and token endpoints must be called over
    HTTPS.

    - **Validate state** – When using the authorization code flow, validate the
    `state` parameter on the callback to prevent CSRF.


    ## Error Handling


    - **400 Bad Request** – Invalid or missing parameters, validation failures,
    or malformed JSON. Response bodies follow the same patterns as other errors
    (flat `error` string, optional `codes`, nested `error` object, or rarely no
    body).


    - **401 Unauthorized** – Invalid or expired access token; refresh the token
    or re-authenticate. JSON bodies are not identical on every route: you may
    see a flat `error` string (sometimes with `codes`), a nested `error` object
    (`type`, `code`, `message`), or rarely an empty body


    - **403 Forbidden** – Insufficient scope or permissions for the requested
    resource. Error JSON may be flat or nested, like 400/401.


    - **404 Not Found** – Resource does not exist or is not visible. Error JSON
    may be flat or nested.


    - **429 Too Many Requests** – Rate limit exceeded; slow down and respect
    `Retry-After` when the header is present. Error JSON may be flat or nested.


    - **500 / 5xx** – Server or upstream failure; retry with backoff. Do not
    depend on a single error JSON shape; some responses may have no body.



    ---


    Endpoints in this spec: health, OAuth2 (authorize, confirm, mfa, token),
    users, accounts, orders, market data, watchlist, chart, analytics,
    calendars, company, economy, financials, indices, options, news, and
    supplemental data.
  title: Aries API — OpenAPI Specification
  version: 1.0.0
servers:
  - description: Production server
    url: https://api.aries.com
security: []
tags:
  - description: >-
      Analytics endpoints for market data analysis including top gainers,
      losers, volume leaders, sector analysis, analyst ratings, market breadth,
      and net inflow
    name: Analytics
  - description: User management and profile endpoints
    name: Users
  - description: >-
      Order management endpoints for placing, updating, canceling, and
      previewing orders
    name: Orders
  - description: Account management endpoints for positions, orders, and balances
    name: Accounts
  - description: Calendar and mergers/acquisitions endpoints
    name: Calendar
  - description: >-
      Market data endpoints for symbol search, real-time data access, and equity
      details
    name: Market Data
  - description: >-
      Watchlist endpoints for listing, creating, updating, and deleting
      watchlists
    name: Watchlist
  - description: Chart endpoints for config, symbols, history, quotes, and server time
    name: Chart
  - description: News and news sentiment endpoints
    name: News
  - description: >-
      Indices endpoints for groups, list, search, bar, bars, chart-bars,
      realtime values
    name: Indices
  - description: Logos search and sync endpoints
    name: Logos
  - description: 'Corporate actions: spinoffs, tender offers, IPO calendar, dividends'
    name: Corporate Actions
  - description: 'Economy endpoints: inflation, inflation expectations, treasury yields'
    name: Economy
  - description: >-
      Options endpoints: expiry dates, contracts, activity, trades, quotes,
      unusual activity
    name: Options
  - description: >-
      Financials: reported, statements, revenue breakdown, short volume, ratios,
      short interest
    name: Financials
  - description: Signals and bull-bear cases
    name: Signals
  - name: Company
  - name: ETF
  - name: Filings
  - name: Market
  - name: Ownership
  - name: Stocks
  - name: Stock Estimates
  - name: Stock Alternative
    description: >-
      Transcripts, company presentation, social sentiment, investment themes,
      supply chain, and ESG data
  - name: Technical Analysis
paths:
  /v1/stock/supply-chain:
    get:
      tags:
        - Stock Alternative
      summary: Get Supply Chain
      description: Retrieve supply chain data for a symbol.
      operationId: get_v1_stock_supply_chain
      parameters:
        - name: symbol
          in: query
          required: true
          schema:
            example: AAPL
            type: string
          description: >-
            Stock ticker to query. Send the plain uppercase symbol exactly as
            you'd type it on a brokerage screen, e.g. `AAPL`, `MSFT`.
      responses:
        '200':
          description: Supply chain data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      type: object
                      properties:
                        country:
                          type: string
                        customer:
                          type: boolean
                        industry:
                          type: string
                        name:
                          type: string
                        oneMonthCorrelation:
                          type: number
                        oneYearCorrelation:
                          type: number
                        sixMonthCorrelation:
                          type: number
                        supplier:
                          type: boolean
                        symbol:
                          type: string
                        threeMonthCorrelation:
                          type: number
                        twoWeekCorrelation:
                          type: number
                        twoYearCorrelation:
                          type: number
                  symbol:
                    type: string
              example:
                symbol: AAPL
                data:
                  - symbol: AAPL
                    name: Xperi Corp.
                    country: US
                    industry: Technology
                    customer: true
                    supplier: true
                    oneMonthCorrelation: 0.35
                    oneYearCorrelation: 0.35
                    sixMonthCorrelation: 0.35
                    threeMonthCorrelation: 0.35
                    twoWeekCorrelation: 0.35
                    twoYearCorrelation: 0.35
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - OAuth2:
            - market:supplemental
components:
  responses:
    BadRequest:
      description: >-
        Invalid query/path/body, malformed JSON, or failed validation.


        **Backend (shared HTTP errors):** Routes that use the shared error
        writer typically return a **nested** JSON object: `error.type` is an
        uppercase category such as `VALIDATION` or `BAD_REQUEST` (see the API
        implementation). Other routes may return the **flat** `ErrorResponse`
        shape (`error` as a string, optional `codes`). The body may rarely be
        empty.


        **OpenAPI:** Every operation’s **400** response references this
        component so the documented schema and examples stay aligned.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/AuthenticationErrorEnvelope'
              - $ref: '#/components/schemas/ErrorResponse'
            description: >-
              400 responses commonly use a nested `error` object (`type`,
              `code`, `message`, optional `details`). Some services still return
              flat `ErrorResponse` (`error` string, optional `codes`).
          examples:
            nested_shared_http_error:
              summary: Nested error (typical for shared WriteHTTPError-style responses)
              value:
                error:
                  type: VALIDATION
                  code: VALIDATION_ERROR
                  message: Query parameter validation failed
                  details:
                    fieldName: field is required
            nested_bad_request:
              summary: Nested error (BAD_REQUEST / invalid JSON)
              value:
                error:
                  type: BAD_REQUEST
                  code: INVALID_JSON
                  message: string
            flat_string:
              summary: Flat error string
              value:
                error: string
            flat_with_codes:
              summary: Flat error with field codes (some services)
              value:
                error: string
                codes:
                  - field: string
                    code: string
                    description: string
    Unauthorized:
      description: >-
        Authentication failed: missing credentials, invalid JWT, or expired
        access token.


        **Response body variants:** Some routes return JSON with a string
        `error` field (and optionally `codes` / `metadata`). Others return JSON
        where `error` is a structured object (`type`, `code`, `message`, and
        optionally `details`, `request_id`). In edge cases (for example certain
        gateway or middleware paths) the response may have **no body** even
        though the status is 401—clients should treat 401 as unauthenticated and
        refresh or re-authenticate regardless of body shape.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorResponse'
              - $ref: '#/components/schemas/AuthenticationErrorEnvelope'
            description: >-
              401 responses may use a flat `ErrorResponse` shape or a nested
              `error` object. Inspect `error`: if it is a string, use the flat
              shape; if it is an object, use the structured shape.
          examples:
            flat_message:
              summary: Flat error string (typical)
              value:
                error: string
            flat_with_codes:
              summary: Flat error with structured codes
              value:
                error: string
                codes:
                  - code: string
                    description: string
            nested_error_object:
              summary: Nested error (shared HTTP error format)
              value:
                error:
                  type: AUTHENTICATION
                  code: INVALID_TOKEN
                  message: string
    Forbidden:
      description: >-
        Authenticated but not allowed to perform this action or access this
        resource (insufficient scope or role).


        **Response body variants:** Flat `ErrorResponse` or nested `error`
        object, same pattern as 400/401.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorResponse'
              - $ref: '#/components/schemas/AuthenticationErrorEnvelope'
            description: 403 responses may use a flat or nested error shape.
          examples:
            flat:
              summary: Flat message
              value:
                error: string
            nested:
              summary: Nested error (shared HTTP error format)
              value:
                error:
                  type: AUTHORIZATION
                  code: INSUFFICIENT_SCOPE
                  message: string
    NotFound:
      description: >-
        The requested resource does not exist or is not visible to this caller.


        **Response body variants:** Flat `ErrorResponse` or nested `error`
        object.
      content:
        application/json:
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorResponse'
              - $ref: '#/components/schemas/AuthenticationErrorEnvelope'
            description: 404 responses may use a flat or nested error shape.
          examples:
            flat:
              summary: Flat message
              value:
                error: string
            nested:
              summary: Nested error (shared HTTP error format)
              value:
                error:
                  type: NOT_FOUND
                  code: RESOURCE_NOT_FOUND
                  message: string
    InternalServerError:
      description: >-
        Unexpected server error or upstream failure. **Retry with exponential
        backoff**; do not assume a specific JSON body shape.


        **Response body variants:** Flat `ErrorResponse`, nested `error` object,
        or occasionally a minimal message. Some paths may return **no body**.
      content:
        application/json:
          examples:
            flat_string:
              summary: Flat error string
              value:
                error: string
            nested_error_object:
              summary: Nested error (shared HTTP error format)
              value:
                error:
                  type: INTERNAL
                  code: INTERNAL_ERROR
                  message: string
          schema:
            oneOf:
              - $ref: '#/components/schemas/ErrorResponse'
              - $ref: '#/components/schemas/AuthenticationErrorEnvelope'
            description: 5xx responses may use a flat or nested error shape.
  schemas:
    AuthenticationErrorEnvelope:
      type: object
      description: >-
        JSON envelope where `error` is a nested object (`type`, `code`,
        `message`, …). Many services use this shape for 400, 401, 403, 404, 429,
        and 5xx when using the shared HTTP error format.
      properties:
        error:
          $ref: '#/components/schemas/AuthenticationErrorDetail'
        meta:
          type: object
          additionalProperties: true
          description: Optional metadata
    ErrorResponse:
      properties:
        codes:
          description: Structured error codes for programmatic handling
          items:
            $ref: '#/components/schemas/ErrorCode'
          type: array
        error:
          type: string
          description: >-
            Error detail as a string. Exact message content is not fixed and
            should not be hard-coded.
          example: string
        metadata:
          additionalProperties: true
          description: Additional error context
          type: object
      type: object
    AuthenticationErrorDetail:
      type: object
      description: >-
        Structured payload for the nested `error` object. Services using the
        shared Go error writer emit uppercase `type` values aligned with error
        categories (for example `VALIDATION`, `BAD_REQUEST`, `AUTHENTICATION`,
        `AUTHORIZATION`, `NOT_FOUND`, `RATE_LIMIT`, `INTERNAL`).
      properties:
        type:
          type: string
          description: >-
            Error category emitted by the shared error writer, such as
            VALIDATION, BAD_REQUEST, AUTHENTICATION, AUTHORIZATION, NOT_FOUND,
            RATE_LIMIT, or INTERNAL.
          example: AUTHENTICATION
        code:
          type: string
          description: Machine-readable code
          example: INVALID_TOKEN
        message:
          type: string
          description: >-
            Error detail as a string. Exact message content is not fixed and
            should not be hard-coded.
          example: string
        details:
          type: object
          additionalProperties: true
          description: Optional extra context
        request_id:
          type: string
          description: Request correlation id when provided
    ErrorCode:
      properties:
        code:
          description: Machine-readable error code
          example: INVALID_PASSWORD
          type: string
        description:
          description: Human-readable description of the error
          example: Password must be between 8 and 64 characters
          type: string
        field:
          description: Field name that caused the error
          example: password
          type: string
      type: object
  securitySchemes:
    OAuth2:
      type: oauth2
      description: >-
        OAuth2 Bearer token: obtain an access token from the token endpoint and
        send it in the Authorization header.
      flows:
        clientCredentials:
          tokenUrl: https://api.aries.com/v1/oauth2/token
          refreshUrl: https://api.aries.com/v1/oauth2/token
          scopes:
            user:information: View user profile and personal details
            account:information: View account balances, positions, and transaction history
            order:execution: Place, modify, and cancel orders
            order:information: View order history and status
            position:information: View current positions and holdings
            market:information: Access live and historical market data
            calendar:information: Access earnings, economic, and market schedule data
            options:information: Access options chains and expiration data
            analytics:information: View analytics, ratings, and market insights
            market:supplemental: >-
              News, company profiles, financials, filings, ETF data, technical
              analysis
            user:management: >-
              Manage user-scoped resources such as watchlists and other saved
              configuration.
        authorizationCode:
          authorizationUrl: https://app.aries.com/oauth2/authorize
          tokenUrl: https://api.aries.com/v1/oauth2/token
          refreshUrl: https://api.aries.com/v1/oauth2/token
          scopes:
            user:information: View user profile and personal details
            account:information: View account balances, positions, and transaction history
            order:execution: Place, modify, and cancel orders
            order:information: View order history and status
            position:information: View current positions and holdings
            market:information: Access live and historical market data
            calendar:information: Access earnings, economic, and market schedule data
            options:information: Access options chains and expiration data
            analytics:information: View analytics, ratings, and market insights
            market:supplemental: >-
              News, company profiles, financials, filings, ETF data, technical
              analysis
            user:management: >-
              Manage user-scoped resources such as watchlists and other saved
              configuration.

````