> ## Documentation Index
> Fetch the complete documentation index at: https://cantonfoundation-feature-integrate-google-analytics.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Scan Global Synchronizer Connectivity API

> Discover Scan and sequencer endpoints across all SVs

export const networkData = {
  mainnet: {
    name: 'MainNet',
    description: 'Production network for live applications. Data is permanent and never reset. Upgrades follow DevNet and TestNet validation.',
    color: '#22c55e',
    versions: {
      splice: '0.5.18',
      damlSdk: '3.4.12',
      pqs: '3.4.6',
      tokenStandard: '1.0.0',
      walletSdk: '1.1.0',
      dappSdk: '1.1.0',
      walletGateway: '1.2.0'
    },
    advanced: {
      minProtocolVersion: '6',
      migrationId: '4',
      darVersions: [{
        name: 'splice-amulet',
        version: '0.1.14'
      }, {
        name: 'splice-wallet',
        version: '0.1.14'
      }, {
        name: 'splice-dso-governance',
        version: '0.1.20'
      }],
      releaseUrl: 'https://github.com/canton-network/splice/releases/tag/0.5.18'
    },
    endpoint: 'scan.sv-1.global.canton.network.sync.global',
    substitutions: {
      splice_cluster: 'main',
      da_hostname: 'global.canton.network.digitalasset.com',
      gsf_sv_url: 'https://sv.sv-1.global.canton.network.sync.global',
      generic_sv_url: 'https://sv.sv-1.global.canton.network.YOUR_SV_SPONSOR',
      gsf_scan_url: 'https://scan.sv-1.global.canton.network.sync.global',
      generic_scan_url: 'https://scan.sv-1.global.canton.network.YOUR_SV_SPONSOR',
      gsf_sequencer_url: 'https://sequencer-MIGRATION_ID.sv-1.global.canton.network.sync.global',
      version: '0.5.18',
      version_literal: '0.5.18',
      chart_version_literal: '0.5.18',
      chart_version_set: 'export CHART_VERSION=0.5.18',
      image_tag_set: 'export IMAGE_TAG=0.5.18',
      image_tag_set_plain: 'export IMAGE_TAG=0.5.18',
      bundle_download_link: {
        label: 'Download Bundle',
        href: 'https://github.com/digital-asset/decentralized-canton-sync/releases/download/v0.5.18/0.5.18_splice-node.tar.gz'
      },
      openapi_download_link: {
        label: 'Download OpenAPI specs',
        href: 'https://github.com/digital-asset/decentralized-canton-sync/releases/download/v0.5.18/0.5.18_openapi.tar.gz'
      },
      helm_repo_prefix: 'oci://ghcr.io/digital-asset/decentralized-canton-sync/helm',
      docker_repo_prefix: 'ghcr.io/digital-asset/decentralized-canton-sync/docker'
    }
  },
  testnet: {
    name: 'TestNet',
    description: 'Pre-production environment for final validation. Requires MainNet approval to join. May be reset periodically.',
    color: '#eab308',
    versions: {
      splice: '0.6.2',
      damlSdk: '3.5.1',
      pqs: '3.5.1-rc6',
      tokenStandard: '1.0.0',
      walletSdk: '1.1.0',
      dappSdk: '1.1.0',
      walletGateway: '1.2.0'
    },
    advanced: {
      minProtocolVersion: '6',
      migrationId: '1',
      darVersions: [{
        name: 'splice-amulet',
        version: '0.1.15'
      }, {
        name: 'splice-wallet',
        version: '0.1.15'
      }, {
        name: 'splice-dso-governance',
        version: '0.1.21'
      }],
      releaseUrl: 'https://github.com/canton-network/splice/releases/tag/0.6.2'
    },
    endpoint: 'scan.sv-1.test.global.canton.network.sync.global',
    substitutions: {
      splice_cluster: 'test',
      da_hostname: 'test.global.canton.network.digitalasset.com',
      gsf_sv_url: 'https://sv.sv-1.test.global.canton.network.sync.global',
      generic_sv_url: 'https://sv.sv-1.test.global.canton.network.YOUR_SV_SPONSOR',
      gsf_scan_url: 'https://scan.sv-1.test.global.canton.network.sync.global',
      generic_scan_url: 'https://scan.sv-1.test.global.canton.network.YOUR_SV_SPONSOR',
      gsf_sequencer_url: 'https://sequencer-MIGRATION_ID.sv-1.test.global.canton.network.sync.global',
      version: '0.6.2',
      version_literal: '0.6.2',
      chart_version_literal: '0.6.2',
      chart_version_set: 'export CHART_VERSION=0.6.2',
      image_tag_set: 'export IMAGE_TAG=0.6.2',
      image_tag_set_plain: 'export IMAGE_TAG=0.6.2',
      bundle_download_link: {
        label: 'Download Bundle',
        href: 'https://github.com/digital-asset/decentralized-canton-sync/releases/download/v0.6.2/0.6.2_splice-node.tar.gz'
      },
      openapi_download_link: {
        label: 'Download OpenAPI specs',
        href: 'https://github.com/digital-asset/decentralized-canton-sync/releases/download/v0.6.2/0.6.2_openapi.tar.gz'
      },
      helm_repo_prefix: 'oci://ghcr.io/digital-asset/decentralized-canton-sync/helm',
      docker_repo_prefix: 'ghcr.io/digital-asset/decentralized-canton-sync/docker'
    }
  },
  devnet: {
    name: 'DevNet',
    description: 'Development environment with latest features. Open to any validator (IP allowlist required). Reset every 3 months. Best for testing upgrades.',
    color: '#a78bfa',
    versions: {
      splice: '0.6.4',
      damlSdk: '3.5.1',
      pqs: '3.5.1-rc6',
      tokenStandard: '1.0.0',
      walletSdk: '1.1.0',
      dappSdk: '1.1.0',
      walletGateway: '1.2.0'
    },
    advanced: {
      minProtocolVersion: '6',
      migrationId: '1',
      darVersions: [{
        name: 'splice-amulet',
        version: '0.1.15'
      }, {
        name: 'splice-wallet',
        version: '0.1.15'
      }, {
        name: 'splice-dso-governance',
        version: '0.1.21'
      }],
      releaseUrl: 'https://github.com/canton-network/splice/releases/tag/0.6.4'
    },
    endpoint: 'scan.sv-1.dev.global.canton.network.sync.global',
    substitutions: {
      splice_cluster: 'dev',
      da_hostname: 'dev.global.canton.network.digitalasset.com',
      gsf_sv_url: 'https://sv.sv-1.dev.global.canton.network.sync.global',
      generic_sv_url: 'https://sv.sv-1.dev.global.canton.network.YOUR_SV_SPONSOR',
      gsf_scan_url: 'https://scan.sv-1.dev.global.canton.network.sync.global',
      generic_scan_url: 'https://scan.sv-1.dev.global.canton.network.YOUR_SV_SPONSOR',
      gsf_sequencer_url: 'https://sequencer-MIGRATION_ID.sv-1.dev.global.canton.network.sync.global',
      version: '0.6.4',
      version_literal: '0.6.4',
      chart_version_literal: '0.6.4',
      chart_version_set: 'export CHART_VERSION=0.6.4',
      image_tag_set: 'export IMAGE_TAG=0.6.4',
      image_tag_set_plain: 'export IMAGE_TAG=0.6.4',
      bundle_download_link: {
        label: 'Download Bundle',
        href: 'https://github.com/digital-asset/decentralized-canton-sync/releases/download/v0.6.4/0.6.4_splice-node.tar.gz'
      },
      openapi_download_link: {
        label: 'Download OpenAPI specs',
        href: 'https://github.com/digital-asset/decentralized-canton-sync/releases/download/v0.6.4/0.6.4_openapi.tar.gz'
      },
      helm_repo_prefix: 'oci://ghcr.io/digital-asset/decentralized-canton-sync/helm',
      docker_repo_prefix: 'ghcr.io/digital-asset/decentralized-canton-sync/docker'
    }
  }
};

export const NetworkVariables = ({children, title = null, description, defaultNetwork = 'devnet', networkData = {}}) => {
  const deploymentNetworkOrder = ['devnet', 'testnet', 'mainnet'];
  const tokenPattern = /\|([A-Za-z0-9_]+)\|/g;
  const tokenTextValue = value => {
    if (value && typeof value === 'object' && ('href' in value)) {
      return value.href;
    }
    return value ?? null;
  };
  const replacementForToken = (token, substitutions) => tokenTextValue(substitutions?.[token]);
  const replaceStringProp = (value, substitutions) => {
    if (typeof value !== 'string') {
      return value;
    }
    return value.replace(tokenPattern, (match, token) => replacementForToken(token, substitutions) ?? match);
  };
  const replaceTextNode = (value, substitutions, keyPrefix = 'token') => {
    if (typeof value !== 'string') {
      return value;
    }
    const pieces = [];
    let lastIndex = 0;
    let match;
    tokenPattern.lastIndex = 0;
    while ((match = tokenPattern.exec(value)) !== null) {
      const [rawToken, token] = match;
      const replacement = substitutions?.[token];
      if (!replacement) {
        continue;
      }
      if (match.index > lastIndex) {
        pieces.push(value.slice(lastIndex, match.index));
      }
      if (replacement && typeof replacement === 'object' && replacement.href) {
        const linkLabel = replacement.label ?? replacement.href;
        const networkLabel = activeData?.versions?.splice ? `${activeData.name ?? activeNetwork} ${activeData.versions.splice}` : activeData?.name ?? activeNetwork;
        pieces.push(<a key={`${keyPrefix}-${pieces.length}`} href={replacement.href}>
            {networkLabel ? `${linkLabel} (${networkLabel})` : linkLabel}
          </a>);
      } else {
        pieces.push(String(replacement));
      }
      lastIndex = match.index + rawToken.length;
    }
    if (!pieces.length) {
      return value;
    }
    if (lastIndex < value.length) {
      pieces.push(value.slice(lastIndex));
    }
    return pieces;
  };
  const replaceNode = (node, substitutions, keyPrefix = 'node') => {
    if (typeof node === 'string') {
      return replaceTextNode(node, substitutions, keyPrefix);
    }
    if (Array.isArray(node)) {
      return node.map((child, index) => replaceNode(child, substitutions, `${keyPrefix}-${index}`));
    }
    if (!React.isValidElement(node)) {
      return node;
    }
    const networkOnly = node.props?.['data-network-only'];
    if (typeof networkOnly === 'string') {
      const allowedNetworks = networkOnly.split(',').map(network => network.trim()).filter(Boolean);
      if (!allowedNetworks.includes(activeNetwork)) {
        return null;
      }
    }
    const props = {};
    for (const [name, value] of Object.entries(node.props ?? ({}))) {
      if (name === 'children') {
        continue;
      }
      props[name] = typeof value === 'string' ? replaceStringProp(value, substitutions) : value;
    }
    return React.cloneElement(node, props, React.Children.map(node.props.children, (child, index) => replaceNode(child, substitutions, `${keyPrefix}-${index}`)));
  };
  const options = deploymentNetworkOrder.filter(networkKey => networkData[networkKey]?.substitutions);
  const defaultIndex = Math.max(0, options.indexOf(defaultNetwork));
  const [activeIndex, setActiveIndex] = React.useState(defaultIndex);
  const activeNetwork = options[activeIndex] ?? options[0];
  const activeData = networkData[activeNetwork];
  const panelRef = React.useRef(null);
  React.useEffect(() => {
    if (!activeData?.substitutions || !panelRef.current) {
      return;
    }
    const replaceTokenText = value => value.replace(/\|([A-Za-z0-9_]+)\|/g, (match, token) => replacementForToken(token, activeData.substitutions) ?? match);
    const replaceRenderedCode = () => {
      for (const codeRoot of panelRef.current.querySelectorAll('pre, code')) {
        if (codeRoot.tagName === 'PRE' && codeRoot.querySelector('code')) {
          continue;
        }
        codeRoot.textContent = replaceTokenText(codeRoot.textContent ?? '');
      }
    };
    let animationFrame;
    const scheduleReplaceRenderedCode = () => {
      if (animationFrame) {
        window.cancelAnimationFrame(animationFrame);
      }
      animationFrame = window.requestAnimationFrame(() => {
        animationFrame = null;
        replaceRenderedCode();
      });
    };
    const observer = new MutationObserver(scheduleReplaceRenderedCode);
    const timeouts = [100, 500, 1000].map(delay => window.setTimeout(replaceRenderedCode, delay));
    replaceRenderedCode();
    observer.observe(panelRef.current, {
      childList: true,
      characterData: true,
      subtree: true
    });
    return () => {
      observer.disconnect();
      if (animationFrame) {
        window.cancelAnimationFrame(animationFrame);
      }
      for (const timeout of timeouts) {
        window.clearTimeout(timeout);
      }
    };
  }, [activeNetwork, activeData]);
  if (!activeData) {
    return null;
  }
  const label = networkKey => {
    const network = networkData[networkKey];
    const version = network?.versions?.splice;
    return version ? `${network?.name ?? networkKey} (${version})` : network?.name ?? networkKey;
  };
  return <div data-component="network-variables" className="my-6 overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-950">
      {(title || description) && <div className="border-b border-gray-200 px-4 py-3 dark:border-gray-800">
          {title && <div className="text-sm font-semibold text-gray-950 dark:text-gray-100">{title}</div>}
          {description && <div className="mt-1 text-sm leading-6 text-gray-600 dark:text-gray-400">{description}</div>}
        </div>}
      <div role="tablist" className="flex gap-1 border-b border-gray-200 bg-gray-50 p-1 dark:border-gray-800 dark:bg-gray-900">
        {options.map((networkKey, index) => {
    const isActive = index === activeIndex;
    return <button key={networkKey} type="button" role="tab" aria-selected={isActive} onClick={() => setActiveIndex(index)} className={`min-h-9 px-3 text-sm font-medium transition-colors ${isActive ? 'rounded-md bg-white text-gray-950 shadow-sm dark:bg-gray-800 dark:text-gray-100' : 'rounded-md text-gray-600 hover:bg-white/80 hover:text-gray-950 dark:text-gray-400 dark:hover:bg-gray-800/70 dark:hover:text-gray-100'}`}>
              {label(networkKey)}
            </button>;
  })}
      </div>
      <div key={activeNetwork} ref={panelRef} role="tabpanel" className="space-y-4 px-4 py-4 text-sm leading-6 text-gray-800 dark:text-gray-200">
        {React.Children.map(children, (child, index) => {
    if (typeof child === 'string' && !child.trim()) {
      return null;
    }
    const content = replaceNode(child, activeData.substitutions, `network-${activeNetwork}-${index}`);
    if (content === null || content === false) {
      return null;
    }
    return <div className="min-w-0">
              {content}
            </div>;
  })}
      </div>
    </div>;
};

Splice network applications and validators need to be able to connect to multiple supervalidators' Scan apps and Canton sequencers. If you have the base URL for a single SV Scan app, you can query for all other connected Scans and the SV sequencers. This lets you dynamically update your app configuration based on changing network SV configurations, and easily check that your own deployments can successfully connect to all needed endpoints. Additionally, you can query for all network-approved validators and get their details.

## Listing all SV Scans

<NetworkVariables networkData={networkData}>
  Every Scan can list all approved SV scans connected to the network. For example, query from /v0/scans from <a href="|gsf_scan_url|">|gsf\_scan\_url|</a>, and the response will be something like

  ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "scans": [
      {
        "domainId": "global-domain::122084177677350389dd0710d6516f700a33fe348c5f2702dffef6d36e1dedcbfc17",
        "scans": [
          /* several scans */,
          {
            "publicUrl": "https://scan.sv.dev.global.canton.network.digitalasset.com",
            "svName": "DA-Helm-Test-Node"
          },
          /* several other scans */,
          {
            "publicUrl": "https://scan.sv-1.dev.global.canton.network.sync.global",
            "svName": "Global-Synchronizer-Foundation"
          }
        ]
      }
    ]
  }
  ```

  `scans` is a list of synchronizer IDs, each with an associated list of SVs and their Scan base URLs. In this case, `Global-Synchronizer-Foundation` matches the originally-used Scan. Take any of these `publicUrl`s and query `/api/scan/v0/scans`, and the same set will be returned.
</NetworkVariables>

## Listing all SV Sequencers

Likewise, Canton sequencers for all approved SVs are published by every Scan. For example, query from /v0/dso-sequencers, and the result will be something like

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "domainSequencers": [
    {
      "domainId": "global-domain::122084177677350389dd0710d6516f700a33fe348c5f2702dffef6d36e1dedcbfc17",
      "sequencers": [
        /* several sequencers */,
        {
          "migrationId": 4,
          "id": "SEQ::DA-Helm-Test-Node::122054dda14cc07dbf8cd56b392263c73b10630c0040f185b737ee5526c8d479ffab",
          "url": "https://sequencer-4.sv.dev.global.canton.network.digitalasset.com",
          "svName": "DA-Helm-Test-Node",
          "availableAfter": "2024-11-29T15:25:05.771558Z"
        },
        /* several other sequencers */,
        {
          "migrationId": 4,
          "id": "SEQ::Global-Synchronizer-Foundation::1220408be05ce20b9f7023ec1cad8af61f0280fd30a050ccf052660da2adf35549ed",
          "url": "https://sequencer-4.sv-1.dev.global.canton.network.sync.global",
          "svName": "Global-Synchronizer-Foundation",
          "availableAfter": "2024-12-02T13:30:27.774745Z"
        }
      ]
    }
  ]
}
```

As `svName` is the same as the `svName` in [Listing all SV Scans](#listing-all-sv-scans), it can be used to join the results of these endpoints, if necessary. `url` is the base URL of that SV's sequencer.

## Listing all Validators

Every validator approved on the network has a "license" on the ledger. The /v0/admin/validator/licenses endpoint lists all of these in the form of `Splice.ValidatorLicense:ValidatorLicense` Daml contracts. These include:

* onboarding identification, in the form of `validator`, `sponsor`, and `dso`,
* activity statistics, such as `faucetState` and `lastActiveAt`, and
* other tertiary validator information in `metadata`.

All of these, and most other important data, are within the `payload` property. Other fields reflect that this is a Daml contract, and are useful for other Daml interactions.

These licenses can only record information about the past state of the network. However, `lastActiveAt` can be used to infer which validators are currently likely to be active on the network and which are inactive or disconnected.

Here's a snippet of an example response from an active network:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "validator_licenses": [
    /* many similar records */,
    {
      "template_id": "979ec710c3ae3a05cb44edf8461a9b4d7dd2053add95664f94fc89e5f18df80f:Splice.ValidatorLicense:ValidatorLicense",
      "contract_id": "00e17d6e36499b656f8e248c31f18130e00db30332820b6b91da9f222049f3d078ca101220d189c2482b5018eae7c656561bf201a54deb276f81e8a0d715b7305d3a03abc3",
      "payload": {
        "dso": "DSO::122084177677350389dd0710d6516f700a33fe348c5f2702dffef6d36e1dedcbfc17",
        "sponsor": "Digital-Asset-2::1220dcf294bffd10439167f7460b1b6926b7a87d3237b748c7de6d7367fad3e33b20",
        "lastActiveAt": "2025-01-23T12:48:21.220193Z",
        "validator": "digitalasset-testValidator-1::1220167abbb792b81e5fb1dc45311f32eb9d796c46a5100a1cd446979176f699e117",
        "faucetState": {
          "firstReceivedFor": {
            "number": "17087"
          },
          "lastReceivedFor": {
            "number": "17122"
          },
          "numCouponsMissed": "0"
        },
        "metadata": {
          "lastUpdatedAt": "2025-01-23T07:03:50.820769Z",
          "version": "0.3.6",
          "contactPoint": "sv-support@example.com"
        }
      },
      "created_event_blob": "CgMyLjESwgYKRQDhfW42SZtlb44kjDHxgTDgDbMDMoILa5HanyIgSfPQeMoQEiDRicJIK1AY6ufGVlYb8gGlTesnb4HooNcVtzBdOgOrwxINc3BsaWNlLWFtdWxldBpuCkA5NzllYzcxMGMzYWUzYTA1Y2I0NGVkZjg0NjFhOWI0ZDdkZDIwNTNhZGQ5NTY2NGY5NGZjODllNWYxOGRmODBmEgZTcGxpY2USEFZhbGlkYXRvckxpY2Vuc2UaEFZhbGlkYXRvckxpY2Vuc2UilQNqkgMKZgpkOmJkaWdpdGFsYXNzZXQtdGVzdFZhbGlkYXRvci0xOjoxMjIwMTY3YWJiYjc5MmI4MWU1ZmIxZGM0NTMxMWYzMmViOWQ3OTZjNDZhNTEwMGExY2Q0NDY5NzkxNzZmNjk5ZTExNwpZClc6VURpZ2l0YWwtQXNzZXQtMjo6MTIyMGRjZjI5NGJmZmQxMDQzOTE2N2Y3NDYwYjFiNjkyNmI3YTg3ZDMyMzdiNzQ4YzdkZTZkNzM2N2ZhZDNlMzNiMjAKTQpLOklEU086OjEyMjA4NDE3NzY3NzM1MDM4OWRkMDcxMGQ2NTE2ZjcwMGEzM2ZlMzQ4YzVmMjcwMmRmZmVmNmQzNmUxZGVkY2JmYzE3CioKKFImCiRqIgoMCgpqCAoGCgQY/ooCCgwKCmoICgYKBBjEiwIKBAoCGAAKQQo/Uj0KO2o5CgsKCSmhJ1o2WiwGAAoJCgdCBTAuMy42Ch8KHUIbc3Ytc3VwcG9ydEBkaWdpdGFsYXNzZXQuY29tCg8KDVILCgkpYWtnBl8sBgAqSURTTzo6MTIyMDg0MTc3Njc3MzUwMzg5ZGQwNzEwZDY1MTZmNzAwYTMzZmUzNDhjNWYyNzAyZGZmZWY2ZDM2ZTFkZWRjYmZjMTcyYmRpZ2l0YWxhc3NldC10ZXN0VmFsaWRhdG9yLTE6OjEyMjAxNjdhYmJiNzkyYjgxZTVmYjFkYzQ1MzExZjMyZWI5ZDc5NmM0NmE1MTAwYTFjZDQ0Njk3OTE3NmY2OTllMTE3OWFrZwZfLAYAQioKJgokCAESIBhgA7BpKZodv42OaTYfxCqk4WQhgRJ+scPIQtmbh96gEB4=",
      "created_at": "2025-01-23T12:48:21.220193Z"
    },
    /* many similar records */,
  ],
  "next_page_token": 6033
}
```

Passing this `next_page_token` as the `after` query parameter, you might see another page; the sequence of pages terminates with an absent or null token as follows:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "validator_licenses": [],
  "next_page_token": null
}
```

## Parties' Hosting Participants

In any Canton deployment, [each party is hosted on a participant](/overview/learn/global-synchronizer-architecture#global-synchronizer-architecture). This can be accessed through Scan with /v0/domains/{domain_id}/parties/{party_id}/participant-id.

For example, looking up `/v0/domains/global-domain::122084177677350389dd0710d6516f700a33fe348c5f2702dffef6d36e1dedcbfc17/parties/digitalasset-testValidator-1::1220e92bbc9d80cb6e283184017b307b9f44f23d32d7d195cdbcac033ae91eac2f28/participant-id` on a test network yields

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "participant_id": "PAR::validator-runbook::1220e92bbc9d80cb6e283184017b307b9f44f23d32d7d195cdbcac033ae91eac2f28"
}
```
