diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml
deleted file mode 100644
index b563e538a..000000000
--- a/.github/workflows/check-links.yml
+++ /dev/null
@@ -1,123 +0,0 @@
-name: Check - broken links
-
-on:
- pull_request:
- types: [opened, reopened, synchronize]
- workflow_dispatch: # Allows manual triggering from the GitHub UI
-
-permissions:
- contents: read
- pull-requests: write
- issues: write
-
-jobs:
- link-check:
- runs-on: ubuntu-latest
- timeout-minutes: 30
- if: github.event_name != 'pull_request' || github.actor != 'codatbot'
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Set up Node.js
- uses: actions/setup-node@v6
- with:
- node-version: '20'
-
- - name: Install linkinator
- run: npm install -g linkinator
- env:
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
-
- - name: Get Vercel Preview URL
- id: get-preview-url
- if: github.event_name == 'pull_request'
- run: |
- # Wait a bit for Vercel deployment to be ready
- sleep 30
-
- # Try to get the preview URL from Vercel API
- if [ -n "${{ secrets.VERCEL_TOKEN }}" ]; then
- # Get the latest deployment for this PR
- DEPLOYMENT=$(curl -s -H "Authorization: Bearer ${{ secrets.VERCEL_TOKEN }}" \
- "https://api.vercel.com/v6/deployments?teamId=${{ secrets.VERCEL_TEAM_ID }}&projectId=${{ secrets.VERCEL_PROJECT_ID }}&gitSource.pullRequestId=${{ github.event.pull_request.number }}" \
- | jq -r '.deployments[0].url // empty')
-
- if [ -n "$DEPLOYMENT" ]; then
- echo "preview_url=https://$DEPLOYMENT" >> $GITHUB_OUTPUT
- else
- # Fallback: construct the preview URL using the typical Vercel pattern
- BRANCH_NAME=$(echo "${{ github.head_ref }}" | sed 's/[^a-zA-Z0-9]/-/g')
- echo "preview_url=https://codat-docs-git-${BRANCH_NAME}-codat.vercel.app" >> $GITHUB_OUTPUT
- fi
- else
- # Fallback: construct the preview URL using the typical Vercel pattern
- BRANCH_NAME=$(echo "${{ github.head_ref }}" | sed 's/[^a-zA-Z0-9]/-/g')
- echo "preview_url=https://codat-docs-git-${BRANCH_NAME}-codat.vercel.app" >> $GITHUB_OUTPUT
- fi
-
- - name: Run linkinator
- run: |
- # Function to run linkinator with a given URL
- run_linkinator() {
- local url=$1
- npx linkinator "$url" --recurse --verbosity error --skip ".*github.*|.*localhost.*" --format json --timeout 10000 --delay 1000 > link-results.json
- }
-
- if [ "${{ github.event_name }}" == "pull_request" ]; then
- # Use preview URL for PRs
- run_linkinator "${{ steps.get-preview-url.outputs.preview_url }}"
- else
- # Use main docs site for manual runs
- run_linkinator "https://docs.codat.io"
- fi
- continue-on-error: true # Ensure the workflow continues even if linkinator finds broken links
-
- - name: Delete Previous Comments
- uses: actions/github-script@v8
- if: github.event_name == 'pull_request'
- with:
- github-token: ${{secrets.GITHUB_TOKEN}}
- script: |
- const issue_number = context.issue.number;
- const owner = context.repo.owner;
- const repo = context.repo.repo;
- const comments = await github.rest.issues.listComments({
- issue_number,
- owner,
- repo,
- });
- const actionComments = comments.data.filter(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('Link check results'));
- for (const comment of actionComments) {
- await github.rest.issues.deleteComment({
- owner,
- repo,
- comment_id: comment.id,
- });
- }
-
- - name: Filter and Post Results
- uses: actions/github-script@v8
- if: github.event_name == 'pull_request'
- with:
- github-token: ${{secrets.GITHUB_TOKEN}}
- script: |
- const fs = require('fs');
- const results = JSON.parse(fs.readFileSync('link-results.json', 'utf8'));
- const filtered = results.links
- .filter(link => link.status !== 403 && link.status !== 500 && link.status !== 0)
-
- const printList = filtered
- .map(link => `[${link.status}] ${link.url}`);
- const previewUrl = '${{ steps.get-preview-url.outputs.preview_url }}';
- const output = `Link check results for preview deployment (${previewUrl}):\n\`\`\`\n${JSON.stringify(printList, null, 2)}\n\`\`\``;
- github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: output,
- });
-
- if (filtered.length > 0) {
- core.setFailed("There are broken links in the documentation.");
- }
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index df28d3e48..45aaf931f 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -2,10 +2,17 @@ name: PR build
on:
pull_request:
+ workflow_dispatch: # Allows manual triggering from the GitHub UI
+
+permissions:
+ contents: read
+ pull-requests: write
+ issues: write
jobs:
build:
runs-on: ubuntu-latest
+ timeout-minutes: 30
steps:
- uses: actions/checkout@v7
with:
@@ -32,3 +39,73 @@ jobs:
GTM_ID: ${{ vars.GTM_ID }}
FEATURE_DEV_FLAG: ${{ vars.FEATURE_DEV_FLAG }}
FEATURE_NEW_PRODUCTS_FLAG: ${{ vars.FEATURE_NEW_PRODUCTS_FLAG }}
+
+ # Link checking reuses the build above: linkinator serves ./build on an
+ # ephemeral localhost port, so no deployed preview environment is needed.
+ # --silent keeps npm's banner out of the JSON on stdout.
+ - name: Check for broken links
+ if: github.actor != 'codatbot'
+ run: npm run --silent links:check -- --format json > link-results.json
+ continue-on-error: true # Broken links are reported below, not here
+
+ - name: Delete previous link check comments
+ uses: actions/github-script@v8
+ if: github.event_name == 'pull_request' && github.actor != 'codatbot'
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ const issue_number = context.issue.number;
+ const owner = context.repo.owner;
+ const repo = context.repo.repo;
+ const comments = await github.rest.issues.listComments({
+ issue_number,
+ owner,
+ repo,
+ });
+ const actionComments = comments.data.filter(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('Link check results'));
+ for (const comment of actionComments) {
+ await github.rest.issues.deleteComment({
+ owner,
+ repo,
+ comment_id: comment.id,
+ });
+ }
+
+ - name: Post link check results
+ uses: actions/github-script@v8
+ if: github.actor != 'codatbot'
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ script: |
+ const fs = require('fs');
+ if (!fs.existsSync('link-results.json') || fs.statSync('link-results.json').size === 0) {
+ core.setFailed("The link check did not produce any results — see the 'Check for broken links' step.");
+ return;
+ }
+ // --verbosity error means the JSON only contains broken links, so
+ // filter out the statuses that bot protection (403) and flaky or
+ // briefly-down external hosts (0, 5xx) return rather than genuine
+ // link rot. Without this the check goes red on other people's
+ // outages.
+ const results = JSON.parse(fs.readFileSync('link-results.json', 'utf8'));
+ const isNoise = status => status === 0 || status === 403 || status >= 500;
+ const filtered = results.links.filter(link => !isNoise(link.status))
+
+ const printList = filtered
+ .map(link => `[${link.status}] ${link.url}`);
+
+ if (context.eventName === 'pull_request') {
+ const output = `Link check results:\n\`\`\`\n${JSON.stringify(printList, null, 2)}\n\`\`\``;
+ await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: output,
+ });
+ } else {
+ core.info(printList.join('\n'));
+ }
+
+ if (filtered.length > 0) {
+ core.setFailed("There are broken links in the documentation.");
+ }
diff --git a/blog/220715-data-coverage-explorer.md b/blog/220715-data-coverage-explorer.md
deleted file mode 100644
index b695634f1..000000000
--- a/blog/220715-data-coverage-explorer.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-title: Use our new Data Coverage Explorer to view and compare our data coverage across all supported accounting, commerce, and banking platforms.
-date: "2022-07-15"
-tags: ["API", "Resource"]
-draft: false
-authors: mcclowes
-link: "https://knowledge.codat.io/supported-features/accounting?integrationKey=gbol"
----
-
-[Read more...](https://knowledge.codat.io/supported-features/accounting?integrationKey=gbol)
-
-
diff --git a/blog/220817-product-update.md b/blog/220817-product-update.md
index 390f4a6e7..9ef732aeb 100644
--- a/blog/220817-product-update.md
+++ b/blog/220817-product-update.md
@@ -16,7 +16,6 @@ We've got a host of Portal and DX improvements for you this week.
- **Update:** We've [updated the navigation bar](https://docs.codat.io/updates/220806-navigation-bar) in the Codat Portal to make your experience more intuitive.
- **New:** We've added a Support button in the navigation bar for quick access to Codat's [support resources](https://codat.zendesk.com/hc/en-gb)
-- **New:** Use our new Data Coverage Explorer to view and compare our data coverage across all supported accounting, commerce, and banking platforms. https://knowledge.codat.io/supported-features/accounting?integrationKey=gbol
#### Assess
diff --git a/blog/230115-dashboard.md b/blog/230115-dashboard.md
index dec14eef3..4a56b5ed1 100644
--- a/blog/230115-dashboard.md
+++ b/blog/230115-dashboard.md
@@ -12,7 +12,7 @@ The Portal homepage dashboard now shows how many data connections in each connec
This is to better highlight your those connections that need your attention, like those that have de-authorized.
-[Read more about connection statuses](https://codat-docs.vercel.app/core-concepts/connections).
+[Read more about connection statuses](/core-concepts/connections).
The Portal dashboard used to combined multiple connection statuses into single data points, making it hard to get a detailed picture of the health of your companies and their data connections.
diff --git a/docs/enterprise/tech-implementation/consent-journey.md b/docs/enterprise/tech-implementation/consent-journey.md
index 06b358317..3f78fae5a 100644
--- a/docs/enterprise/tech-implementation/consent-journey.md
+++ b/docs/enterprise/tech-implementation/consent-journey.md
@@ -16,14 +16,6 @@ To address this, Codat provides you with Link, our consent and authorization jou

-:::info Sample consent journey
-
-We prepared a consent journey prototype using an example business insights dashboard use case.
-
-[View the prototype in full screen →](https://www.figma.com/proto/YWkKvsYgeHJskPsfuIpy7w/Codat---Generic-bank---Consent-Journey?page-id=601%3A4488&type=design&node-id=641-11421&viewport=1275%2C-2886%2C0.1&t=rrDznIIhmQ8EayyY-1&scaling=contain&starting-point-node-id=641%3A11421&mode=design)
-
-:::
-
### Implementation options
Codat offers two options to implement the Link solution in your application:
diff --git a/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md b/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md
index 373cf1514..4f57821d6 100644
--- a/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md
+++ b/docs/integrations/accounting/quickbooksonline/accounting-quickbooksonline.md
@@ -40,7 +40,7 @@ Each tier is allocated a monthly limit of CorePlus API calls, and exceeding this
To avoid unexpected costs, track your app's usage of API calls following these steps:
-1. Log in to the [Intuit Developer Portal](https://developer.intuit.com/dashboard?tab=apps).
+1. Log in to the [Intuit Developer Portal](https://accounts.intuit.com/app/sign-in).
2. Navigate to the _App dashboard_ and select the app you want to review.
3. In the left-hand menu, click **Analytics**.
4. In _Analytics_, toggle the environment to **Production**. You will see a chart detailing your Core and CorePlus usage.
diff --git a/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md b/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md
index a556e5f33..b57837b9e 100644
--- a/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md
+++ b/docs/integrations/accounting/quickbooksonline/faq-quickbooks-online.md
@@ -35,7 +35,7 @@ When the Options endpoint is called for one of these data types and an AST-enabl
## Is QuickBooks Online (FR) supported?
:::caution QBO no longer available in France
-Note that QuickBooks Online (FR) will no longer be available after December 31st, 2023, as communicated by [Intuit](https://quickbooks.intuit.com/learn-support/fr-fr/help-article/account-management/faq/L5GgPEpLf_FR_fr_FR).
+Note that QuickBooks Online (FR) will no longer be available after December 31st, 2023, as communicated by Intuit.
:::
Yes. Codat supports the same functionality for QuickBooks Online France (FR) as for QuickBooks Online UK and US.
diff --git a/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md b/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md
index f491d451f..c4a5a15ef 100644
--- a/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md
+++ b/docs/integrations/accounting/zoho-books/accounting-zohobooks-setup.md
@@ -35,7 +35,7 @@ If you already have a Zoho Books account, have your account details to hand. If
To register your Zoho Books application.
-1. Go to [https://api-console.zoho.com/](https://api-console.zoho.com/) and sign in to Zoho Books. If you are using your account for the first time, select **GET STARTED**.
+1. Go to [https://accounts.zoho.com/signin](https://accounts.zoho.com/signin) and sign in to Zoho Books. If you are using your account for the first time, select **GET STARTED**.
The **Developer Console** is displayed. If the **Applications** screen is displayed instead, from the top-left corner, select **ADD CLIENT**.
2. Select **Server-based Applications**.
The **Create New Client** page is displayed.
diff --git a/package.json b/package.json
index f30199def..c192e4f21 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,7 @@
"format:js:check": "prettier --check \"**/*.{js,jsx,ts,tsx}\"",
"format:mdx": "prettier --write \"**/*.{md,mdx}\"",
"format:mdx:check": "prettier --check \"**/*.{md,mdx}\"",
- "links:check": "linkinator ./build --recurse --verbosity error --skip \".*github.*|.*localhost.*\"",
+ "links:check": "linkinator ./build --recurse --verbosity error --timeout 20000 --concurrency 25 --retry --skip \".*github.*\"",
"vale:sync": "vale sync",
"vale": "vale docs/",
"vale:check": "vale --minAlertLevel=warning docs/",