# ProofHub

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to ProofHub using the API Calls tab — create an API Group pointing to your account-specific base URL (https://YOURCOMPANY.proofhub.com/api/v3/), add static headers for X-API-KEY and User-Agent, then call REST endpoints to read projects and to-do lists. No OAuth required. For production, proxy the API key through a Firebase Cloud Function to keep it out of the compiled app.

## Build a Mobile Project Dashboard Backed by ProofHub

ProofHub's flat-fee pricing — $45/mo for the Essential plan with unlimited users, or $89/mo for Ultimate Control — makes it attractive for growing teams who want predictable costs. Unlike tools that charge per seat, ProofHub bundles tasks, discussions, file proofing, Gantt charts, and time tracking into one subscription. Building a mobile companion app in FlutterFlow lets your team members check and update tasks from iOS or Android without opening a browser.

The ProofHub REST v3 API is straightforward to call: every request goes to your account-specific subdomain (https://YOURCOMPANY.proofhub.com/api/v3/), and authentication is a single static header — X-API-KEY. The one wrinkle most tutorials miss is a second required header: User-Agent. ProofHub silently rejects requests that arrive without a User-Agent string, which looks like a mysterious 400 or 403 with no helpful error body. Setting both headers in the API Group fixes it.

FlutterFlow's API Calls panel handles the entire integration visually — no terminal, no code until you optionally add a Firebase Cloud Function proxy for the key. You will define an API Group (base URL + shared headers), then add individual API Calls for listing projects, fetching to-do lists, and creating tasks. Response data maps to FlutterFlow Data Types via JSON Path expressions, which then power ListViews and form submissions in your app's UI.

## Before you start

- A ProofHub account (Essential or Ultimate Control plan) with API access enabled
- Your ProofHub API key — generated in Manage Profile (click your profile picture five times) or Account Settings → API
- Your ProofHub account subdomain — the YOURCOMPANY part of https://YOURCOMPANY.proofhub.com
- A FlutterFlow project (any plan) with at least one screen
- Optional: a Firebase project with Cloud Functions enabled if you want to proxy the key for production

## Step-by-step guide

### 1. Generate your ProofHub API key and note your base URL

Log into ProofHub in your browser and navigate to your profile. The API key is tucked away: click your profile picture (top right) five times in quick succession, and a new option appears — API. Alternatively, go to Account Settings → API if your plan shows it there. Click 'Generate new key' and copy the key to a safe place such as a password manager or your notes app. Do not store it in a code file or commit it to version control.

While you are here, note your account subdomain. Your ProofHub login URL is https://YOURCOMPANY.proofhub.com — the YOURCOMPANY segment is the company slug you chose when signing up. This becomes the first part of every API call you make. The v3 base URL is: https://YOURCOMPANY.proofhub.com/api/v3/ — note the trailing slash. There is also a legacy v1 API at https://api.proofhub.com/v1/ but v3 has richer endpoints for projects and to-do lists, so always use v3 unless you have a specific reason not to. Write down both the key and the full base URL before switching to FlutterFlow.

**Expected result:** You have a ProofHub API key copied and your account-specific base URL written down (e.g., https://acmecorp.proofhub.com/api/v3/).

### 2. Create an API Group in FlutterFlow with both required headers

Open your FlutterFlow project in the browser. In the left navigation bar, click API Calls (the plug icon, or find it in the left panel list). Click + Add → Create API Group. A dialog appears asking for a name and base URL. Name it something clear like 'ProofHub' and paste your full base URL — for example https://acmecorp.proofhub.com/api/v3/ — in the Base URL field.

Now add the two headers that ProofHub requires on every request. Click the Headers tab inside the API Group editor. Add the first header:
- Header name: X-API-KEY
- Value: your ProofHub API key

Then add the second header — this one is critical and often missed:
- Header name: User-Agent
- Value: FlutterFlowApp/1.0 (yourname@yourcompany.com)

ProofHub rejects requests that arrive without a recognizable User-Agent string with a silent 400 or 403, with no body explaining why. Adding a User-Agent identifying your app (and optionally a contact email per good API citizenship) solves this immediately.

At this stage, resist the temptation to hardcode the API key directly as a static string if you plan to release the app. Dart code and API Call configurations are compiled into the app binary, which means anyone who unpacks the APK or IPA can extract the key. For now, a static header is fine for prototyping — see Step 6 for the production proxy approach.

```
// API Group configuration (reference — set these values in the FlutterFlow UI)
// Group name: ProofHub
// Base URL: https://YOURCOMPANY.proofhub.com/api/v3/
// Headers (static):
//   X-API-KEY: your-api-key-here
//   User-Agent: FlutterFlowApp/1.0 (you@company.com)
```

**Expected result:** An API Group named 'ProofHub' appears in the API Calls panel with the base URL and both headers configured. The group shows a green checkmark or no error indicators.

### 3. Add GET calls for Projects and To-do Lists, map JSON to Data Types

Inside the ProofHub API Group, click + Add API Call. Name the first call 'Get Projects'. Set the method to GET and the endpoint to projects (the full URL becomes https://YOURCOMPANY.proofhub.com/api/v3/projects). Click the Response & Test tab. Paste a sample ProofHub JSON response into the sample JSON field — you can get one by calling the API directly in a browser REST client like Hoppscotch or by pasting your key into ProofHub's API documentation examples. Click Generate JSON Paths. FlutterFlow will parse the array and generate path selectors like $[*].id, $[*].title, $[*].description.

Now create a Data Type to hold project data. Go to the Data Types section in the left nav → + Add Data Type, name it 'ProofHubProject', and add fields: id (Integer), title (String), description (String). Come back to your API Call, go to the Response tab, and map each JSON Path to the corresponding Data Type field.

Repeat this process for a second call: 'Get ToDo Lists'. The endpoint is todolists (FlutterFlow auto-prefixes the base URL). ProofHub to-do lists belong to a project, so the endpoint is actually projects/{project_id}/todolists — add a variable: in the Variables tab, add a path variable named project_id. Now your endpoint field reads projects/{{project_id}}/todolists. In the JSON response, map fields like id, title, and status into a second Data Type named 'ProofHubTodoList'.

Test each call in the Test tab by entering real values for any variables. You should see a 200 OK and a populated JSON response. If you see a 400 or 403, double-check that both headers are set correctly in the parent API Group.

```
// JSON Paths to map after pasting a sample response:
// For projects array:
// $[*].id         → ProofHubProject.id (Integer)
// $[*].title      → ProofHubProject.title (String)
// $[*].description → ProofHubProject.description (String)
//
// For todolists array:
// $[*].id         → ProofHubTodoList.id (Integer)
// $[*].title      → ProofHubTodoList.title (String)
// $[*].status     → ProofHubTodoList.status (String)
```

**Expected result:** Two API Calls (Get Projects and Get ToDo Lists) appear in the API Group, each showing a 200 OK in the test panel with JSON Paths mapped to Data Type fields.

### 4. Bind project data to a ListView and add a to-do detail screen

Now wire the data to your FlutterFlow UI. Open the screen where you want to display ProofHub projects. Add a ListView widget from the widget palette. Select the ListView, open the Backend Query tab (right panel → Data → Backend Query), choose API Call, select 'Get Projects' from the dropdown, and set the query to run once on page load.

Inside the ListView, add a ListTile or a custom Row layout. Select a Text widget that should show the project title. In the Set from Variable panel, choose 'Project Item → title' from the list — FlutterFlow automatically creates the item reference when you set the ListView's data source. Do the same for the description field or any other field you mapped.

For the to-do list detail, create a new screen called ToDoListScreen. Add a pass-through parameter (Integer type, named project_id) in the screen's Parameters tab. Add another ListView on this screen backed by the 'Get ToDo Lists' API Call, passing the project_id parameter as the path variable. From the first screen's ListTile, set the Navigate To action → ToDoListScreen → pass the selected project's id as the project_id argument.

This pattern — a master list screen that drills into a detail screen passing an ID as a parameter — is the standard FlutterFlow pattern for REST API data and works cleanly with ProofHub's project → to-do list hierarchy.

**Expected result:** The app's project list screen loads and displays ProofHub project titles. Tapping a project navigates to a to-do list screen populated with that project's lists.

### 5. Add a POST call to create a new to-do item from a form

Creating a task in ProofHub requires a POST request to the to-do endpoint: projects/{project_id}/todolists/{todolist_id}/todos. In the API Calls panel, add a third API Call inside the ProofHub group. Set method to POST and the endpoint to projects/{{project_id}}/todolists/{{todolist_id}}/todos. In the Variables tab, add three variables: project_id (Integer, path), todolist_id (Integer, path), and title (String, body).

In the Body tab, select JSON body type and enter the payload structure. FlutterFlow lets you construct JSON bodies by typing field names and referencing your variables with {{variable_name}} syntax. The minimum required body is:
{ "todo": { "title": "{{title}}" } }

Back in your app, create a simple form screen or a bottom sheet with a TextField for the to-do title and a Button labeled 'Add Task'. Select the Button, open the Actions panel, click + Add Action, choose Backend/API Calls → Create To-Do (your new POST call). Map the project_id and todolist_id from your screen parameters, and map the title from the TextField controller. After the action, add a Navigate Back action or a Show Snack Bar to confirm success.

Test this in FlutterFlow's Test Mode. If the POST returns 201 Created, the task appears in ProofHub immediately. If it returns a 4xx, open the Test tab of the API Call and inspect the full response body — ProofHub usually returns a JSON error object describing the problem (missing required field, wrong list ID, etc.).

```
// POST /projects/{{project_id}}/todolists/{{todolist_id}}/todos
// Method: POST
// Headers: inherited from API Group (X-API-KEY + User-Agent)
// Body (JSON):
{
  "todo": {
    "title": "{{title}}"
  }
}
// Optional additional fields:
// "description": "{{description}}"
// "assignees": [{ "id": {{assignee_id}} }]
// "duedate": "{{due_date}}"
```

**Expected result:** Tapping 'Add Task' creates a new item in the specified ProofHub to-do list. A success snack bar appears in the app, and refreshing the to-do list screen shows the new item.

### 6. Move the API key to a Firebase Cloud Function proxy for production

For any app you release to the App Store or Play Store, the API key must not ship inside the compiled binary. Anyone who decompiles an APK or uses a network proxy can extract keys stored in Flutter app state or hardcoded in API Call headers. Since ProofHub API keys grant full access to your account's projects and tasks, a leaked key is a serious risk.

The solution is a lightweight Firebase Cloud Function that holds the key server-side and forwards requests to ProofHub. In your Firebase project, create an HTTPS Cloud Function (Node.js). The function accepts a path parameter from your FlutterFlow app, appends it to the ProofHub base URL, injects the X-API-KEY and User-Agent headers from environment config (not hardcoded in code either), and returns the ProofHub response.

In FlutterFlow, update your API Group's base URL to point to the Cloud Function's URL instead of ProofHub directly. The Function URL looks like https://us-central1-YOUR-PROJECT.cloudfunctions.net/proofhubProxy. Your FlutterFlow API Calls no longer contain the ProofHub key at all — the proxy handles it. If you want to restrict which FlutterFlow users can call the proxy, add Firebase Auth token validation in the Cloud Function using admin.auth().verifyIdToken().

This pattern — FlutterFlow → your Cloud Function → third-party API — is the recommended production architecture for any secret key integration in FlutterFlow. If you prefer not to set up Firebase, RapidDev's team builds FlutterFlow integrations like this every week and offers a free scoping call at rapidevelopers.com/contact.

```
// Firebase Cloud Function proxy (Node.js, index.js)
const functions = require('firebase-functions');
const axios = require('axios');

exports.proofhubProxy = functions.https.onRequest(async (req, res) => {
  res.set('Access-Control-Allow-Origin', '*');
  if (req.method === 'OPTIONS') {
    res.set('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE');
    res.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.status(204).send('');
    return;
  }

  const apiKey = functions.config().proofhub.apikey;
  const companySlug = functions.config().proofhub.company;
  const baseUrl = `https://${companySlug}.proofhub.com/api/v3`;
  const path = req.query.path || '';

  try {
    const response = await axios({
      method: req.method,
      url: `${baseUrl}/${path}`,
      headers: {
        'X-API-KEY': apiKey,
        'User-Agent': 'FlutterFlowProxy/1.0 (admin@yourcompany.com)',
        'Content-Type': 'application/json'
      },
      data: req.body
    });
    res.status(response.status).json(response.data);
  } catch (error) {
    const status = error.response ? error.response.status : 500;
    res.status(status).json(error.response ? error.response.data : { error: 'Proxy error' });
  }
});
```

**Expected result:** FlutterFlow API Calls hit the Cloud Function URL. The function forwards requests to ProofHub and returns the response. The ProofHub API key is never present in the compiled Flutter app.

## Best practices

- Never hardcode the X-API-KEY value in a FlutterFlow widget's action or App State for a production release — use a Firebase Cloud Function proxy so the key stays server-side.
- Always set a descriptive User-Agent header (including a contact email) in your API Group; it is required by ProofHub and good API citizenship for any service.
- Use the API Call test panel to verify each endpoint with real data before binding to widgets — this catches JSON path mismatches early.
- Store project_id and todolist_id as page parameters rather than App State so that each screen instance has its own isolated context when navigating through a project hierarchy.
- Add empty-state widgets to every ListView that reads from ProofHub — an empty response is valid (a new account has no projects) and must not look like a loading failure.
- Implement a rate-limit awareness strategy for your most-called endpoints: cache the project list in App State for a few minutes rather than re-fetching on every screen open, since ProofHub's rate limits are undocumented and may be tighter than expected.
- For POST and PATCH calls, always inspect the full API response in the test panel — ProofHub returns validation errors in the response body as a JSON object, not just an HTTP status code.

## Use cases

### Team task dashboard for field crews

A construction or facilities company builds a mobile FlutterFlow app that loads all active ProofHub projects and their to-do lists. Field crew members can see what tasks are assigned to them, mark items complete, and add comments — all from a phone without needing browser access to ProofHub.

Prompt example:

```
Build a Flutter app screen that loads a list of ProofHub projects, lets the user tap a project to see its to-do lists, and lets them check off individual tasks.
```

### Client-facing project status portal

An agency creates a branded mobile app for clients to check project milestones without logging into ProofHub directly. The FlutterFlow app calls the ProofHub API read-only, filters tasks by milestone stage, and displays a clean progress view.

Prompt example:

```
Build a status tracker screen that reads a single ProofHub project's to-do lists and shows completion percentage per list with a progress bar.
```

### Rapid task creation from a form

A product team's FlutterFlow app includes a quick-capture form: the user enters a task title and assigns it to a list. A button press fires a POST request to ProofHub's to-do API, creating the item instantly and confirming success with a SnackBar notification.

Prompt example:

```
Build a task creation form in FlutterFlow with a text field for title and a dropdown for to-do list selection, then POST the new task to ProofHub when the user taps 'Add Task'.
```

## Troubleshooting

### API call returns 400 or 403 with an empty or unhelpful body

Cause: The most common cause is a missing User-Agent header. ProofHub silently rejects requests without a recognizable User-Agent string and returns a minimal error body that gives no hint about the real problem.

Solution: Open the ProofHub API Group in the API Calls panel, go to the Headers tab, and confirm that User-Agent is present with a non-empty value. Add it if missing: 'FlutterFlowApp/1.0 (you@company.com)'. Re-run the test — the call should return 200.

### 404 Not Found on every API call

Cause: The base URL is using a generic api.proofhub.com path (v1) instead of the account-specific subdomain required by v3. The v3 API does not exist at the shared host.

Solution: Update the API Group's Base URL to https://YOURCOMPANY.proofhub.com/api/v3/ — replacing YOURCOMPANY with your actual company slug. The slug is the subdomain you use when you log in. The v1 generic URL only works with the older v1 endpoint set and is missing most v3 features.

### 401 Unauthorized after the app was working fine

Cause: ProofHub API keys can be regenerated by the account owner. If the key was rotated in Manage Profile, all existing integrations using the old key will fail with 401.

Solution: Go back to your ProofHub profile, click your profile picture five times to reveal the API menu, and check whether a new key was generated. Update the key in your FlutterFlow API Group header (or Firebase Cloud Function config if you are using the proxy).

### ListView shows no items even though the API test returns data

Cause: The JSON Path mapping or the Data Type binding may be mismatched. If the API returns a root-level array and you used $.items[*].title instead of $[*].title, the path finds nothing.

Solution: Open the API Call in the test panel, paste the actual response JSON, and click Generate JSON Paths again. Compare the generated paths to what you typed manually. The correct paths for ProofHub project lists typically start at $[*] (the root array), not $.data[*] or $.projects[*]. Re-map the Data Type fields to match.

## Frequently asked questions

### Does ProofHub have an official FlutterFlow integration or plugin?

No. There is no official FlutterFlow connector or plugin for ProofHub. The integration is built manually using FlutterFlow's API Calls panel to make direct REST requests to the ProofHub v3 API. This tutorial covers that exact pattern.

### Which ProofHub plan gives me API access?

Both the Essential ($45/month flat) and Ultimate Control ($89/month flat) plans include API access. ProofHub does not have a free plan with API access, and the pricing is per-account (unlimited users), not per seat. Verify the current plan details on proofhub.com before purchasing.

### Can I use ProofHub webhooks to push data into my FlutterFlow app in real time?

ProofHub does not expose inbound webhooks that can push events directly into a FlutterFlow app. Your FlutterFlow app will need to poll the ProofHub API periodically to detect changes, or you can set up a Firebase Cloud Function that FlutterFlow calls to get the latest data. Real-time push is not available from the ProofHub side.

### Why do I get a 400 error even though my API key is correct?

The most common cause is a missing User-Agent header. ProofHub requires both X-API-KEY and User-Agent on every request, and silently returns a 400 or 403 when User-Agent is absent. Add 'User-Agent: FlutterFlowApp/1.0 (you@company.com)' as a static header in your API Group and the error should resolve.

### Can multiple team members share one API key, or does each user need their own?

ProofHub API keys are per-user — each account holder generates their own key. If your FlutterFlow app acts as a backend integration on behalf of one admin user (a common pattern for team dashboards), you can use that user's key server-side in a Cloud Function. For apps where each end-user logs in with their own ProofHub account, you would need to handle per-user key storage securely, which is significantly more complex.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/proofhub
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/proofhub
