Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.taqtile.com/llms.txt

Use this file to discover all available pages before exploring further.

Exporting Manifest Faults to Excel with Office Scripts

This article walks through manifest_fault_excel_automate.ts, an Office Script that pulls fault records from the Taqtile Manifest API and writes them directly into the active Excel worksheet. It also explains how you can adapt the script to build other reports against the same API. Unlike the Python equivalent, this script runs entirely inside Excel for the web through the Automate tab — no local Python installation, no pip install, and no separate .xlsx output file. The workbook you are looking at is the report.

What the Script Does

At a high level, the script:
  1. Authenticates with the Manifest REST API to get a JWT token
  2. Sends a single GraphQL query to fetch the first 100 faults for the configured organization
  3. Writes a header row and one row per fault to the active worksheet
The output appears immediately in the worksheet you ran the script from, starting at cell A1.
manifest_fault_excel_automate.ts file:
interface LoginResponse {
    user: {
        token: string;
    };
}

interface Asset {
    id: number;
    serialNumber: string;
}

interface Fault {
    id: number;
    description: string;
    createdByUserId: number;
    resolvedByUserId: number | null;
    createdAt: string;
    resolvedAt: string | null;
    asset: Asset | null;
}

interface GraphQLResponse {
    data: {
        getFaults: Fault[];
    };
}

async function main(workbook: ExcelScript.Workbook): Promise<void> {
    // Step 1: Login
    const loginRes = await fetch("https://[domain].taqmanifest.com/rest/signin/v3", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ email: "username@domain.com", password: "password" })
    });

    if (!loginRes.ok) {
        const errorText: string = await loginRes.text();
        throw new Error(`Login failed (${loginRes.status}): ${errorText}`);
    }

    const loginData: LoginResponse = await loginRes.json() as LoginResponse;
    const token: string = loginData.user.token;

    if (!token) {
        throw new Error(`Login succeeded but no token found. Response: ${JSON.stringify(loginData)}`);
    }

    // Step 2: GraphQL query
    const gqlRes = await fetch("https://[domain].taqmanifest.com/graphql/v3", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
            "Authorization": token
        },
        body: JSON.stringify({
            query: "{ getFaults(itemsPerPage: 100, pageNumber: 1, orgId: [1]) { id description createdByUserId resolvedByUserId createdAt resolvedAt asset { id serialNumber } } }"
        })
    });

    if (!gqlRes.ok) {
        const errorText: string = await gqlRes.text();
        throw new Error(`GraphQL failed (${gqlRes.status}): ${errorText}`);
    }

    const gqlData: GraphQLResponse = await gqlRes.json() as GraphQLResponse;
    const faults: Fault[] = gqlData.data.getFaults;

    // Step 3: Write to sheet
    const sheet: ExcelScript.Worksheet = workbook.getActiveWorksheet();
    sheet.getRange("A1:H1").setValues([["ID", "Description", "Created By", "Resolved By", "Created At", "Resolved At", "Asset ID", "Asset Serial"]]);

    faults.forEach((fault: Fault, i: number) => {
        sheet.getRange(`A${i + 2}:H${i + 2}`).setValues([[
            fault.id,
            fault.description ?? "",
            fault.createdByUserId ?? "",
            fault.resolvedByUserId ?? "",
            fault.createdAt ?? "",
            fault.resolvedAt ?? "",
            fault.asset ? fault.asset.id : "",
            fault.asset ? fault.asset.serialNumber : ""
        ]]);
    });
}

Requirements

  • Excel for the web with an Microsoft 365 (Business or Enterprise) license that includes the Automate tab. Office Scripts are not available in the desktop or mobile versions of Excel.
  • A Manifest user account with permission to read faults for the target organization.
  • Your Manifest subdomain, email, password, and target organization ID.
No additional packages or installations are required — Office Scripts run in a sandboxed TypeScript runtime hosted by Microsoft.

Usage

  1. Open the workbook you want the report written to in Excel for the web.
  2. Select the Automate tab in the ribbon, then choose New Script.
  3. Paste the contents of manifest_fault_excel_automate.ts into the Code Editor pane, replacing the default main stub.
  4. Edit the three placeholders near the top of the script:
    • [domain] — your Manifest subdomain (used in both the REST and GraphQL URLs)
    • username@domain.com — your Manifest login email
    • password — your Manifest password
  5. If your data lives under a different organization, update orgId: [1] in the GraphQL query to the correct ID.
  6. Click Save script, give it a name (for example, Manifest Faults Export), then click Run.
The first time you run the script, Excel will prompt you to allow it to make external network requests. You must approve this for the API calls to succeed. When the run finishes, the active worksheet will contain a header row in row 1 and one fault per row starting at row 2.

How It Works, Step by Step

1. Authentication

The script POSTs credentials to the Manifest REST endpoint:
POST /rest/signin/v3
Content-Type: application/json

{ "email": "...", "password": "..." }
The response JSON contains user.token, which is a JWT used as the Authorization header on the GraphQL request that follows. If the login call fails, or the response succeeds but contains no token, the script throws and Excel surfaces the error in the run pane.

2. Fetching Faults

A single GraphQL query is sent to the Manifest GraphQL endpoint:
{
  getFaults(itemsPerPage: 100, pageNumber: 1, orgId: [1]) {
    id
    description
    createdByUserId
    resolvedByUserId
    createdAt
    resolvedAt
    asset {
      id
      serialNumber
    }
  }
}
The query is intentionally compact — it requests the first 100 faults for organization 1 and returns only the fields the report needs. There is no pagination loop in this script: if you have more than 100 faults and need them all, see Adapting the Script below.
Note on the Authorization header: Office Scripts use the standard browser fetch API, and the token is passed directly as the Authorization header value (no Bearer prefix), matching the convention used by the Manifest API.

3. Writing to the Worksheet

The script writes results to the active worksheet — whichever sheet is selected when you press Run. It uses two Range operations:
  • A single setValues call writes the header row to A1:H1.
  • A forEach over the faults writes each row individually with setValues on A{n}:H{n}.
The columns are:
ColumnSource
IDfault.id
Descriptionfault.description
Created Byfault.createdByUserId
Resolved Byfault.resolvedByUserId
Created Atfault.createdAt
Resolved Atfault.resolvedAt
Asset IDfault.asset.id
Asset Serialfault.asset.serialNumber
null values are coerced to empty strings using the ?? operator so the cells render blank rather than showing null. Faults with no associated asset write blank cells in the last two columns.
Note on dates: createdAt and resolvedAt are written exactly as the API returns them (ISO 8601 strings). Excel will display them as text. To convert them into native Excel datetimes, use a formula in an adjacent column such as =DATEVALUE(LEFT(E2,10))+TIMEVALUE(MID(E2,12,8)), or modify the script to parse and write a Date object.

Adapting the Script for Other Reports

The script is structured in three clearly separated phases — auth, query, write — making it straightforward to swap in different queries or output shapes.

Change the GraphQL Query

The query is a plain string passed in the body of the gqlRes fetch call. To report on a different resource, replace the query string and update the data extraction path and Fault interface to match the new shape:
// Example: query assets instead of faults
interface AssetRecord {
    id: number;
    name: string;
    assetClass: { title: string } | null;
}

interface AssetResponse {
    data: { assets: AssetRecord[] };
}

const gqlRes = await fetch("https://[domain].taqmanifest.com/graphql/v3", {
    method: "POST",
    headers: { "Content-Type": "application/json", "Authorization": token },
    body: JSON.stringify({
        query: "{ assets(itemsPerPage: 100, pageNumber: 1) { id name assetClass { title } } }"
    })
});

const gqlData: AssetResponse = await gqlRes.json() as AssetResponse;
const records: AssetRecord[] = gqlData.data.assets;

Add or Remove Columns

Two places control the columns: the header row written to A1:H1, and the row written inside forEach. To add a column, extend both arrays in the same order, and widen the range from H to I (or further). Keeping the header and the data row in sync is the only constraint.

Paginate Through All Results

The script fetches only the first page. To pull every fault, wrap the GraphQL call in a loop and increment pageNumber until a page returns fewer than itemsPerPage records:
const allFaults: Fault[] = [];
let page = 1;
const PAGE_SIZE = 100;

while (true) {
    const gqlRes = await fetch("https://[domain].taqmanifest.com/graphql/v3", {
        method: "POST",
        headers: { "Content-Type": "application/json", "Authorization": token },
        body: JSON.stringify({
            query: `{ getFaults(itemsPerPage: ${PAGE_SIZE}, pageNumber: ${page}, orgId: [1]) { id description createdByUserId resolvedByUserId createdAt resolvedAt asset { id serialNumber } } }`
        })
    });
    const gqlData: GraphQLResponse = await gqlRes.json() as GraphQLResponse;
    const batch = gqlData.data.getFaults;
    allFaults.push(...batch);
    if (batch.length < PAGE_SIZE) break;
    page += 1;
}

Filter Client-Side

Once the data is in memory, filter with standard Array.prototype.filter calls before writing. For example, to show only unresolved faults:
const openFaults: Fault[] = faults.filter(f => f.resolvedAt === null);

Write Faster with a Single setValues Call

The current script writes one row at a time, which is the slowest pattern in Office Scripts. For larger result sets, build a 2-D array first and write it all at once:
const rows: (string | number)[][] = faults.map(f => [
    f.id,
    f.description ?? "",
    f.createdByUserId ?? "",
    f.resolvedByUserId ?? "",
    f.createdAt ?? "",
    f.resolvedAt ?? "",
    f.asset ? f.asset.id : "",
    f.asset ? f.asset.serialNumber : ""
]);
sheet.getRange(`A2:H${rows.length + 1}`).setValues(rows);
This is dramatically faster for hundreds or thousands of rows.

Format the Output

Office Scripts can also style the cells they write. For example, bolding the header row and auto-fitting the columns:
const header = sheet.getRange("A1:H1");
header.getFormat().getFont().setBold(true);
sheet.getRange("A:H").getFormat().autofitColumns();

Point at a Different Environment

The two fetch URLs control the target environment. Change them to switch between staging and production:
const BASE = "https://app.taqmanifest.com";       // production
// const BASE = "https://test.taqmanifest.com";   // staging
Then build the REST and GraphQL URLs from the constant. Office Scripts admins may also need to allow the destination domain in the Microsoft 365 admin center if external API access is restricted.

Security Note

The script currently has credentials embedded as string literals inside main. Office Scripts do not have access to environment variables, OS-level secret stores, or the Excel Identity APIs that desktop add-ins use, so true secret management is not available in this runtime. For shared or recurring use, prefer one of these approaches:
  • Pass credentials as parameters when running through Power Automate. Office Scripts called from a flow can accept arguments, letting you store the email and password in a secured Power Automate connection or environment variable rather than in the script body.
  • Restrict the script’s audience by saving it to a personal OneDrive rather than a shared SharePoint library.
  • Use a service account with the minimum permissions required to read the data the report needs, rather than a privileged user account.
Avoid sharing the script file to a public location while it still contains real credentials.

Summary

manifest_fault_excel_automate.ts demonstrates the typical pattern for building Manifest reports inside Excel for the web:
  1. Authenticate once via REST to get a token
  2. Run a GraphQL query for the resource you want
  3. Write the result directly into the active worksheet with Range.setValues
Because each phase is independent, swapping in a different query, adding pagination, or reformatting the output requires changes in only one or two places — making it a good starting template for any report you want to build against the Manifest API without leaving the browser.