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

# Sage Accounting

> Connect to your users' Sage Accounting systems

export const IntegrationsCompatibility = ({workflows = true, actionkit = false, proxy = true, managedSync = false, authType = "Basic Auth", integrationName: integrationNameProp, integrationSlug: integrationSlugProp}) => {
  const FEATURE_REQUEST_ENDPOINT = "https://agosnlmllwykglihfhiw.supabase.co/functions/v1/handle-vote";
  const FEATURE_REQUEST_HEADERS = {
    "Content-Type": "application/json",
    "Authorization": `Bearer sb_publishable_DlIzCjWe8NiqjZnLziegbg_P-w5L9X4`,
    'apikey': `sb_pubishable_DlIzCjWe8NiqjZnLziegbg_P-w5L9X4`
  };
  const SESSION_VOTE_PREFIX = "paragon_compat_vote:";
  const PURPLE = "rgb(102, 69, 230)";
  const slugifyFeature = label => label.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
  const integrationKeyFromPathname = pathname => {
    if (!pathname) return "";
    const match = pathname.match(/\/(?:resources\/)?integrations\/([^/?#]+)/);
    return match ? decodeURIComponent(match[1]).replace(/\/$/, "") : "";
  };
  const voteStorageKey = (integrationKey, featureKey) => `${SESSION_VOTE_PREFIX}${integrationKey}:${featureKey}`;
  const readVoteFromStorage = (integrationKey, featureKey) => {
    if (typeof sessionStorage === "undefined") return false;
    try {
      return sessionStorage.getItem(voteStorageKey(integrationKey, featureKey)) === "1";
    } catch {
      return false;
    }
  };
  const writeVoteToStorage = (integrationKey, featureKey) => {
    try {
      sessionStorage.setItem(voteStorageKey(integrationKey, featureKey), "1");
    } catch {}
  };
  const readPageTitleFallback = () => {
    if (typeof document === "undefined") return "";
    const h1 = document.querySelector("article h1") || document.querySelector("main h1") || document.querySelector('[class*="title"] h1') || document.querySelector("h1");
    const text = h1?.textContent?.trim();
    return text || "";
  };
  const getRuntimeConfig = () => {
    if (typeof window === "undefined") return null;
    return window.__PARAGON_FEATURE_REQUEST__ ?? null;
  };
  const resolveEndpoint = () => {
    const rt = getRuntimeConfig();
    if (rt?.endpoint) return String(rt.endpoint).trim();
    return String(FEATURE_REQUEST_ENDPOINT || "").trim();
  };
  const resolveHeaders = () => {
    const rt = getRuntimeConfig();
    const base = {
      ...FEATURE_REQUEST_HEADERS,
      ...rt?.headers && typeof rt.headers === "object" ? rt.headers : {}
    };
    return base;
  };
  const isValidEmail = s => (/^[^\s@]+@[^\s@]+\.[^\s@]+$/).test(String(s).trim());
  const renderCompatCheckSvg = (sizePx = 18) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" fill={PURPLE} style={{
    width: `${sizePx}px`,
    height: `${sizePx}px`,
    flexShrink: 0,
    display: "block",
    verticalAlign: "middle",
    margin: "0 auto"
  }} aria-hidden>
      <path d="M320 576C178.6 576 64 461.4 64 320C64 178.6 178.6 64 320 64C461.4 64 576 178.6 576 320C576 461.4 461.4 576 320 576zM438 209.7C427.3 201.9 412.3 204.3 404.5 215L285.1 379.2L233 327.1C223.6 317.7 208.4 317.7 199.1 327.1C189.8 336.5 189.7 351.7 199.1 361L271.1 433C276.1 438 282.9 440.5 289.9 440C296.9 439.5 303.3 435.9 307.4 430.2L443.3 243.2C451.1 232.5 448.7 217.5 438 209.7z" />
    </svg>;
  const products = useMemo(() => [{
    label: "Managed Sync",
    value: managedSync
  }, {
    label: "ActionKit",
    value: actionkit
  }, {
    label: "Workflows",
    value: workflows
  }, {
    label: "Proxy API",
    value: proxy
  }, {
    label: "Auth Type",
    value: authType
  }], [actionkit, managedSync, workflows, proxy, authType]);
  const [mounted, setMounted] = useState(false);
  const [resolvedIntegrationKey, setResolvedIntegrationKey] = useState(() => integrationSlugProp?.trim() || "");
  const [resolvedDisplayName, setResolvedDisplayName] = useState(() => integrationNameProp?.trim() || "Integration");
  const [inlineFormOpen, setInlineFormOpen] = useState(false);
  const [formFeature, setFormFeature] = useState({
    featureLabel: "",
    featureKey: ""
  });
  const [email, setEmail] = useState("");
  const [description, setDescription] = useState("");
  const [submitting, setSubmitting] = useState(false);
  const [submitError, setSubmitError] = useState("");
  const [voteClickError, setVoteClickError] = useState("");
  const [voteLogging, setVoteLogging] = useState(false);
  const [loggingFeatureKey, setLoggingFeatureKey] = useState("");
  const [, bumpVoteUi] = useState(0);
  const inlineCardRef = useRef(null);
  useEffect(() => {
    setMounted(true);
    const path = typeof window !== "undefined" ? window.location.pathname : "";
    const fromPath = integrationSlugProp?.trim() || integrationKeyFromPathname(path);
    setResolvedIntegrationKey(fromPath);
    const fromDom = readPageTitleFallback();
    const name = integrationNameProp?.trim() || fromDom || (fromPath ? fromPath.replace(/-/g, " ") : "Integration");
    setResolvedDisplayName(name);
  }, [integrationNameProp, integrationSlugProp]);
  const dismissInlineForm = useCallback(() => {
    setInlineFormOpen(false);
    setSubmitError("");
  }, []);
  const handleRequestClick = useCallback(async ({featureLabel, featureKey}) => {
    setVoteClickError("");
    const integrationKey = resolvedIntegrationKey || integrationKeyFromPathname(typeof window !== "undefined" ? window.location.pathname : "");
    if (!integrationKey) {
      setVoteClickError("Could not determine integration. Set the integrationSlug prop on this page.");
      return;
    }
    if (readVoteFromStorage(integrationKey, featureKey)) {
      return;
    }
    setLoggingFeatureKey(featureKey);
    setVoteLogging(true);
    const votedAt = new Date().toISOString();
    const ctaPayload = {
      integration_key: integrationKey,
      integration_name: resolvedDisplayName,
      feature_key: featureKey,
      feature_label: featureLabel,
      vote_phase: "cta_click",
      voted_at: votedAt
    };
    try {
      const endpoint = resolveEndpoint();
      if (endpoint) {
        const res = await fetch(endpoint, {
          method: "POST",
          headers: resolveHeaders(),
          body: JSON.stringify(ctaPayload)
        });
        if (!res.ok) {
          const text = await res.text().catch(() => "");
          throw new Error(text || `Request failed (${res.status})`);
        }
      } else if (typeof console !== "undefined" && console.warn) {
        console.warn("[IntegrationsCompatibility] FEATURE_REQUEST_ENDPOINT is empty; vote not sent. Set endpoint or window.__PARAGON_FEATURE_REQUEST__.");
      }
      writeVoteToStorage(integrationKey, featureKey);
      bumpVoteUi(v => v + 1);
      setFormFeature({
        featureLabel,
        featureKey
      });
      setEmail("");
      setDescription("");
      setSubmitError("");
      setInlineFormOpen(true);
    } catch (err) {
      setVoteClickError(err instanceof Error ? err.message : "Could not log your vote. Please try again.");
    } finally {
      setVoteLogging(false);
      setLoggingFeatureKey("");
    }
  }, [resolvedIntegrationKey, resolvedDisplayName]);
  useEffect(() => {
    if (!inlineFormOpen) return undefined;
    const id = window.setTimeout(() => {
      inlineCardRef.current?.scrollIntoView?.({
        behavior: "smooth",
        block: "nearest"
      });
    }, 80);
    return () => window.clearTimeout(id);
  }, [inlineFormOpen]);
  const submitEnrichment = useCallback(async () => {
    const trimmed = email.trim();
    if (!isValidEmail(trimmed)) {
      setSubmitError("Please enter a valid work email.");
      return;
    }
    const integrationKey = resolvedIntegrationKey || integrationKeyFromPathname(typeof window !== "undefined" ? window.location.pathname : "");
    if (!integrationKey) {
      setSubmitError("Could not determine integration. Set the integrationSlug prop on this page.");
      return;
    }
    const {featureKey, featureLabel} = formFeature;
    const endpoint = resolveEndpoint();
    const enrichedAt = new Date().toISOString();
    const payload = {
      integration_key: integrationKey,
      integration_name: resolvedDisplayName,
      feature_key: featureKey,
      feature_label: featureLabel,
      vote_phase: "enrichment",
      email: trimmed,
      description: description.trim() || undefined,
      voted_at: enrichedAt,
      enriched_at: enrichedAt
    };
    setSubmitting(true);
    setSubmitError("");
    try {
      if (endpoint) {
        const res = await fetch(endpoint, {
          method: "POST",
          headers: resolveHeaders(),
          body: JSON.stringify(payload)
        });
        if (!res.ok) {
          const text = await res.text().catch(() => "");
          throw new Error(text || `Request failed (${res.status})`);
        }
      } else if (typeof console !== "undefined" && console.warn) {
        console.warn("[IntegrationsCompatibility] FEATURE_REQUEST_ENDPOINT is empty; enrichment not sent. Set endpoint or window.__PARAGON_FEATURE_REQUEST__.");
      }
      setInlineFormOpen(false);
      setEmail("");
      setDescription("");
    } catch (err) {
      setSubmitError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
    } finally {
      setSubmitting(false);
    }
  }, [email, description, formFeature, resolvedDisplayName, resolvedIntegrationKey]);
  const renderCompatProductCell = ({product, integrationKey, onRequestClick, alignWide, voteLogging, loggingFeatureKey}) => {
    const isAuthType = product.label === "Auth Type";
    const href = typeof product.value === "string" && !isAuthType ? product.value : null;
    const featureKey = slugifyFeature(product.label);
    const alreadyVoted = !isAuthType && integrationKey && !href && !product.value ? readVoteFromStorage(integrationKey, featureKey) : false;
    const unsupported = !isAuthType && !href && !product.value;
    const cellInner = isAuthType ? product.value : href ? <div style={{
      display: "inline-flex",
      alignItems: "center",
      gap: "6px"
    }}>
        {renderCompatCheckSvg()}
        <a href={href} className="compat-doc-link">
          Docs
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M7 17l9.2-9.2M17 17V7H7" /></svg>
        </a>
      </div> : product.value ? renderCompatCheckSvg() : alreadyVoted ? <span style={{
      fontSize: "12px",
      fontWeight: 500,
      color: "rgba(55, 55, 58, 0.85)"
    }} className="compat-requested-text">Requested</span> : <button type="button" className="compat-pill" disabled={voteLogging && loggingFeatureKey === featureKey} onClick={() => onRequestClick({
      featureLabel: product.label,
      featureKey
    })} style={{
      cursor: voteLogging && loggingFeatureKey === featureKey ? "not-allowed" : "pointer",
      opacity: voteLogging && loggingFeatureKey === featureKey ? 0.65 : 1
    }}>
        <svg className="compat-pill-plus" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" style={{
      color: PURPLE
    }} aria-hidden>
          <line x1="12" y1="5" x2="12" y2="19"></line>
          <line x1="5" y1="12" x2="19" y2="12"></line>
        </svg>
        {voteLogging && loggingFeatureKey === featureKey ? "Submitting..." : "Request"}
      </button>;
    if (alignWide) {
      return <div style={{
        fontSize: "13px",
        padding: "8px 8px",
        minHeight: unsupported || alreadyVoted ? "40px" : undefined,
        flex: 1,
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        textAlign: "center"
      }}>
          {cellInner}
        </div>;
    }
    return <span style={{
      fontSize: "13px",
      width: "80px",
      textAlign: "center",
      display: "inline-flex",
      justifyContent: "center"
    }}>
        {cellInner}
      </span>;
  };
  return <div style={{
    backgroundColor: "transparent",
    border: "0px solid rgba(225, 225, 229, 1)",
    borderRadius: "12px",
    padding: "0px 0px",
    overflow: "hidden"
  }}>
      <style>{`
        .compat-wide { display: flex; }
        .compat-narrow { display: none; }
        @media (max-width: 640px) {
          .compat-wide { display: none !important; }
          .compat-narrow { display: flex !important; }
        }
        
        .compat-doc-link {
          color: rgb(102, 69, 230);
          font-weight: 500;
          font-size: 13px;
          display: inline-flex;
          align-items: center;
          gap: 2px;
          text-decoration: none;
        }
        .compat-doc-link:hover {
          text-decoration: underline;
          text-underline-offset: 2px;
        }
        .dark .compat-doc-link {
          color: rgb(162, 140, 255);
        }

        .compat-pill {
          display: inline-flex;
          align-items: center;
          justify-content: center;
          gap: 4px;
          box-sizing: border-box;
          min-height: 32px;
          padding: 6px 12px;
          border-radius: 999px;
          border: 1px solid rgba(0, 0, 0, 0.12);
          background-color: rgba(250, 250, 249, 1);
          font-size: 12px;
          font-weight: 500;
          color: rgba(35, 35, 38, 1);
          font-family: inherit;
          line-height: 1.2;
          width: auto;
          text-decoration: none;
          margin: 0;
          transition: box-shadow 0.15s ease, background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
        }
        .compat-pill:hover:not(:disabled) {
          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        }
        
        a.compat-doc-link {
          text-decoration: underline !important;
          text-underline-offset: 2px !important;
          border-bottom: none !important;
          box-shadow: none !important;
          background: none !important;
        }
        a.compat-doc-link:hover {
          opacity: 0.8;
        }

        .dark .compat-pill {
          background-color: rgba(255, 255, 255, 0.05);
          border-color: rgba(255, 255, 255, 0.1);
          color: rgba(255, 255, 255, 0.85);
        }
        .dark .compat-pill:hover:not(:disabled) {
          box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
          background-color: rgba(255, 255, 255, 0.1);
        }
        .dark .compat-pill-plus {
          color: rgba(255, 255, 255, 0.85) !important;
        }
        .dark .compat-requested-text {
          color: rgba(255, 255, 255, 0.6) !important;
        }

        .compat-form-card {
          margin-top: 24px;
          width: 100%;
          box-sizing: border-box;
          background-color: #fff;
          border-radius: 16px;
          border: 1px solid rgba(0, 0, 0, 0.1);
          box-shadow: 0 8px 24px rgba(0,0,0,0.06);
          padding: 24px;
        }
        .dark .compat-form-card {
          background-color: #0f1114;
          border-color: rgba(255, 255, 255, 0.1);
          box-shadow: 0 8px 24px rgba(0,0,0,0.4);
        }

        .compat-form-close-btn {
          flex-shrink: 0;
          display: inline-flex;
          align-items: center;
          justify-content: center;
          width: 36px;
          height: 36px;
          margin: 0;
          padding: 0;
          border-radius: 8px;
          background: #fff;
          color: rgba(35, 35, 38, 0.75);
          font-size: 24px;
          line-height: 1;
          font-family: inherit;
          border: none;
        }
        .dark .compat-form-close-btn {
          background: rgba(255,255,255,0.05);
          color: rgba(255,255,255,0.7);
        }

        .compat-form-title {
          margin: 0 0 16px;
          font-size: 20px;
          font-weight: 700;
          color: #111;
        }
        .dark .compat-form-title {
          color: rgba(255, 255, 255, 0.95);
        }

        .compat-form-text {
          display: block;
          margin: 0 0 20px;
          font-size: 14px;
          color: rgba(55, 55, 58, 0.72);
          line-height: 1.5;
        }
        .dark .compat-form-text {
          color: rgba(255, 255, 255, 0.6);
        }

        .compat-form-label {
          display: flex;
          flex-direction: column;
          gap: 6px;
          font-size: 13px;
          font-weight: 500;
          color: #333;
        }
        .dark .compat-form-label {
          color: rgba(255, 255, 255, 0.85);
        }

        .compat-form-input-readonly {
          width: 100%;
          box-sizing: border-box;
          padding: 10px 12px;
          border-radius: 8px;
          border: 1px solid rgba(0, 0, 0, 0.08);
          background-color: rgba(250, 248, 245, 0.95);
          font-size: 14px;
          color: rgba(35, 35, 38, 0.95);
        }
        .dark .compat-form-input-readonly {
          background-color: rgba(255, 255, 255, 0.05);
          border-color: rgba(255, 255, 255, 0.1);
          color: rgba(255, 255, 255, 0.7);
        }

        .compat-form-input {
          width: 100%;
          box-sizing: border-box;
          padding: 10px 12px;
          border-radius: 8px;
          border: 1px solid rgba(0, 0, 0, 0.15);
          background-color: #fff;
          font-size: 14px;
          font-family: inherit;
          color: #111;
        }
        .dark .compat-form-input {
          background-color: rgba(255, 255, 255, 0.05);
          border-color: rgba(255, 255, 255, 0.15);
          color: rgba(255, 255, 255, 0.95);
        }

        .compat-form-submit {
          padding: 10px 16px;
          border-radius: 8px;
          border: 1px solid rgba(0, 0, 0, 0.15);
          background: #fff;
          font-size: 14px;
          font-weight: 700;
          font-family: inherit;
          color: #111;
        }
        .dark .compat-form-submit {
          background: rgba(255, 255, 255, 0.1);
          border-color: rgba(255, 255, 255, 0.2);
          color: rgba(255, 255, 255, 0.95);
        }
        .compat-form-tag {
          display: inline-flex;
          align-items: center;
          gap: 6px;
          padding: 6px 10px;
          border-radius: 8px;
          background-color: rgba(102, 69, 230, 0.12);
          border: 1px solid rgba(102, 69, 230, 0.35);
          color: rgb(102, 69, 230);
          font-size: 12px;
          font-weight: 600;
        }
        .dark .compat-form-tag {
          background-color: rgba(162, 140, 255, 0.15);
          border-color: rgba(162, 140, 255, 0.4);
          color: rgb(180, 160, 255);
        }
      `}</style>

      <div className="compat-wide" style={{
    flexDirection: "row",
    gap: "0px"
  }}>
        {products.map(f => <div key={f.label} style={{
    flex: 1,
    minWidth: 0,
    textAlign: "center",
    display: "flex",
    flexDirection: "column"
  }}>
            <div style={{
    fontSize: "13px",
    fontWeight: 500,
    padding: "6px 8px",
    borderBottom: "1px solid rgba(102, 69, 230, 0.15)",
    overflow: "hidden",
    textOverflow: "ellipsis",
    whiteSpace: "nowrap",
    textAlign: "center"
  }}>
              {f.label}
            </div>
            {renderCompatProductCell({
    product: f,
    integrationKey: mounted ? resolvedIntegrationKey : "",
    onRequestClick: handleRequestClick,
    alignWide: true,
    voteLogging,
    loggingFeatureKey
  })}
          </div>)}
      </div>

      <div className="compat-narrow" style={{
    flexDirection: "column",
    gap: "0px"
  }}>
        <div style={{
    display: "flex",
    justifyContent: "space-between",
    borderBottom: "1px solid rgba(102, 69, 230, 0.15)",
    padding: "6px 0"
  }}>
          <span style={{
    fontSize: "13px",
    fontWeight: 500
  }}>Product</span>
          <span style={{
    fontSize: "13px",
    fontWeight: 500,
    width: "80px",
    textAlign: "center"
  }}>Supported</span>
        </div>
        {products.map(f => <div key={f.label} style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    padding: "5px 0"
  }}>
            <span style={{
    fontSize: "13px"
  }}>{f.label}</span>
            {renderCompatProductCell({
    product: f,
    integrationKey: mounted ? resolvedIntegrationKey : "",
    onRequestClick: handleRequestClick,
    alignWide: false,
    voteLogging,
    loggingFeatureKey
  })}
          </div>)}
      </div>

      {voteClickError ? <p style={{
    margin: "16px 0 0",
    fontSize: "13px",
    color: "#b42318"
  }} role="alert">
          {voteClickError}
        </p> : null}

      {inlineFormOpen ? <div ref={inlineCardRef} role="region" aria-labelledby="compat-feature-request-title" className="compat-form-card">
          <div style={{
    display: "flex",
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    gap: "12px",
    marginBottom: "16px"
  }}>
            <div className="compat-form-tag">
              Feature request
            </div>
            <button type="button" onClick={dismissInlineForm} disabled={submitting} aria-label="Close feature request form" className="compat-form-close-btn" style={{
    cursor: submitting ? "not-allowed" : "pointer"
  }}>
              <span aria-hidden>×</span>
            </button>
          </div>
          <h2 id="compat-feature-request-title" className="compat-form-title">
            Request support for {formFeature.featureLabel}
          </h2>
          <p className="compat-form-text">
            Your vote has been recorded. Add your email and an optional note and we'll notify you when support is added.
          </p>

          <div style={{
    display: "flex",
    flexDirection: "column",
    gap: "16px"
  }}>
            <label className="compat-form-label">
              Integration
              <input type="text" readOnly value={resolvedDisplayName} className="compat-form-input-readonly" tabIndex={-1} />
            </label>
            <label className="compat-form-label">
              Feature
              <input type="text" readOnly value={formFeature.featureLabel} className="compat-form-input-readonly" tabIndex={-1} />
            </label>
            <label className="compat-form-label">
              Work email
              <input type="email" name="email" autoComplete="email" placeholder="you@company.com" value={email} onChange={e => setEmail(e.target.value)} className="compat-form-input" disabled={submitting} />
            </label>
            <label className="compat-form-label">
              Describe your use case (optional)
              <textarea placeholder="What would you build if this were supported?" value={description} onChange={e => setDescription(e.target.value)} rows={4} className="compat-form-input" style={{
    resize: "vertical",
    minHeight: "96px"
  }} disabled={submitting} />
            </label>
          </div>

          {submitError ? <p style={{
    margin: "14px 0 0",
    fontSize: "13px",
    color: "#b42318"
  }} role="alert">
              {submitError}
            </p> : null}

          <div style={{
    display: "flex",
    justifyContent: "flex-end",
    gap: "10px",
    marginTop: "22px",
    flexWrap: "wrap"
  }}>
            <button type="button" onClick={submitEnrichment} disabled={submitting} className="compat-form-submit" style={{
    cursor: submitting ? "not-allowed" : "pointer"
  }}>
              {submitting ? "Submitting…" : "Submit details"}
            </button>
          </div>
        </div> : null}
    </div>;
};

<IntegrationsCompatibility workflows={true} actionkit={false} proxy={true} managedSync={false} authType="OAuth2" />

## Setup Guide

<Info>
  **Note:** You'll need to create a new ClickUp app if you don't already have one.
</Info>

You can find your Sage Accounting app credentials in your [Sage Accounting Developer Account.](https://developer.sage.com/api/accounting/api/)

You'll need the following information to set up your Sage Accounting App with Paragon Connect:

* Client ID
* Client Secret
* Scopes Requested

<Info>
  This is an **API-only** integration - workflow actions for this integration are still in development. You can still connect user accounts, build workflows, and access the API for this integration.
</Info>

### Add the Redirect URL to your Sage Accounting app

Paragon provides a redirect URL to send information to your Sage Accounting app. To add the redirect URL to your Sage Accounting app:

1. Copy the link under "**Redirect URL"** in your integration settings in Paragon. The Redirect URL is:

```
https://passport.useparagon.com/oauth
```

2. Log into your [Sage Accounting Developer Portal](https://developer.sage.com/api/accounting/api/).

3. Navigate to **My Account > Applications** and select your application.

4. Under **App Details**, click the edit button.

5. Under **Callback URLs**, paste the Redirect URL provided in Step 1.

ServiceNow provides you with the **Client ID** and **Client Secret** needed for the next steps after adding the redirect URL to your project.

### Add your Sage Accounting app to Paragon

Under **Integrations > Connected Integrations > Sage Accounting > App Configuration > Configure**, fill out your credentials from your developer app in their respective sections:

* **Client ID** Found under App Credentials > Client ID on your Sage Accounting App page.
* **Client Secret:** Found under App Credentials > Client Secret on your Sage Accounting App page.
* **Permissions:** Select the scopes you've requested for your application. For a list of recommended scopes, please view this integration within your Paragon dashboard. [View dashboard.](https://dashboard.useparagon.com)

<Info>
  Leaving the Client ID and Client Secret blank will use Paragon development keys.
</Info>

<Frame>
  <img src="https://mintcdn.com/paragon/JtnB3_DeUOyB_qxZ/assets/Connecting%20your%20Sage%20Accounting%20app%20to%20Paragon%20Connect.png?fit=max&auto=format&n=JtnB3_DeUOyB_qxZ&q=85&s=04ea107c9bc3e77b86d3c95ff03dd1e1" alt="" width="1866" height="1158" data-path="assets/Connecting your Sage Accounting app to Paragon Connect.png" />
</Frame>

## Connecting to Sage Accounting

Once your users have connected their Sage Accounting account, you can use the Paragon SDK to access the Sage Accounting API on behalf of connected users.

See the Sage Accounting [REST API documentation](https://developer.sage.com/api/accounting/api/) for their full API reference.

Any Sage Accounting API endpoints can be accessed with the Paragon SDK as shown in this example.

```javascript theme={null}
// You can find your project ID in the Overview tab of any Integration

// Authenticate the user
paragon.authenticate(<ProjectId>, <UserToken>);
            
// Search for a contact by an email address
paragon.request("sageaccounting", "/contacts?email=<Email>", {
  method: "GET"
});

// Create an invoice
paragon.request("sageaccounting", "/sales_invoices", {
  method: "POST",
  body: {
    contact_id: "<Contact ID>",
    date: "2022-01-31",
    invoice_lines: [{
      description: "Item 1",
      ledger_account_id: "<Ledger Account ID>",
      unit_price: "25.99"
    }]
  }
});

// Create a payment for an invoice
paragon.request("sageaccounting", "/contact_payments", {
  method: "POST",
  body: {
    contact_payment: {
      transaction_type_id: "CUSTOMER_RECEIPT",
      payment_method_id: "CREDIT_DEBIT",
      contact_id: "<Contact ID>",
      bank_account_id: "<Bank Account ID>",
      date: "2022-02-01",
      total_amount: "25.99",
      allocated_artefacts: [{
        artefact_id: "<Invoice ID>",
        amount: "25.99"
      }]
    }
  }
});
  
```

## Building Sage Accounting workflows

Once your Sage Accounting account is connected, you use the [Sage Accounting Request](/workflows/requests#making-integration-requests) step to access any of Sage Accounting's API endpoints without the authentication piece.

When creating or updating records in Sage Accounting, you can reference data from previous steps by typing `{{` to invoke the variable menu.
