Skip to main content

Complete Workflow (Step-by-Step)

This page describes the entire report creation flow in one place: from authentication to retrieving the final report. Use it as a single reference when integrating the API.

Path parameters are written here as :reportId, :source, etc.; in the API and OpenAPI spec the same segments use curly-brace placeholders (see API reference).

In a Nutshell​

  1. Sign in → get a JWT token.
  2. Create report → send a search (username + source, or name + state), receive a report ID.
  3. Get profiles → fetch matched profiles for that report ID (use the same token).
  4. Add profiles → add one or more profile IDs to the report, by source.
  5. Confirm → lock the selection and start loading; get estimated time.
  6. Wait → poll GET /check/:reportId or use SSE until the report is ready.
  7. Get report → fetch the full report and related endpoints (friends, interests, summary).

Important: Send Authorization: Bearer <token> on every request after sign-in (including GET requests). Missing the header on step 3 or later leads to 403 "access denied".


1. Authenticate​

Call POST /auth/signin with your account email and password, and "token": true to receive a JWT in the response. Store that token and send it in the Authorization: Bearer <token> header on all subsequent requests. Token lifetime is 24 hours by default (or 30 days with remember: true). See Authentication for details and Google OAuth.


2. Create Report (Get Report ID)​

Call POST /create with the token and a search payload:

  • By username (recommended): { "source": "ig", "username": "jenniferaniston" }. Sources: ig (Instagram), tw (Twitter), fb (Facebook), tt (TikTok), li (LinkedIn).
  • By name (US): { "name": "John Smith", "state": "CA" } to search across networks.

The response is { "id": "<reportId>" }. Save this report ID; it is used in all following steps.


3. Retrieve Matched Profiles​

Call GET /create/:reportId to get all matched profiles from all sources, or GET /create/:reportId/:source for a single source with optional limit and offset. You must send the Bearer token on this GET request; otherwise the server returns 403 "access denied".

Each profile has an id, source, and may have a badReason if it cannot be analyzed (private, notFound, zeroFollows, optOut, unloaded, unmapped, unexpected). If no profile was found for the initial search, you can call POST /create/:reportId/:source/search with another username.


4. Add Profiles to the Report​

For each profile you want to include, call POST /create/:reportId/:source/add with body { "id": "<profile_id>" }. There is no bulk "add all" endpoint: to add several accounts from different social networks to the same report, you must call this endpoint once per account (one request per source/username). For example, to add one Instagram and one Twitter profile, call add for the Instagram profile, then call add again for the Twitter profile. The response includes a status (inited, inprogress, done, error) per profile. Optionally, call GET /create/:reportId/:source again to refresh the list after adding.


5. Confirm Selection​

When you are done adding profiles, call POST /create/:reportId/confirm with a JSON array of the selected profiles, e.g.:

[ { "source": "ig", "id": "ig_profile_id" }, { "source": "tw", "id": "tw_profile_id" } ].

You cannot skip add: the confirm body must list exactly the profiles that were already added in step 4 (same source and id). The API does not accept a list of new accounts in confirm only—it validates that each entry matches the profile currently stored for that source in the report. If you send an id that was not added, or omit a source you added, the server may return an error. Add each profile first, then call confirm with that same list.

This starts data loading. The response includes estimatedTime (milliseconds until data is ready; 0 means already loaded) and loadTimeout. You cannot change the report after confirmation, and calling confirm again on the same report returns 409 ("report profiles were already confirmed").

Confirming itself does not spend a report credit — the credit check happens earlier, on POST /create (see the 402 FAQ below). The credit is actually deducted transparently the first time you fetch the finished report (GET /report/:reportId); there is no separate "spend" call.

Confirm requires a signed-in account, not just a session. Steps 2–4 work for an anonymous (guest) session, but POST /create/:reportId/confirm returns 401 if the caller has no real user ID — sign in with POST /auth/signin before calling confirm.


6. Wait Until the Report Is Ready​

If estimatedTime > 0, wait before fetching the report. You can:

  • Poll: GET /check/:reportId until ready === true (and generated === true when the full report including summary is available).
  • SSE (recommended): GET /sse/:reportId with Accept: text/event-stream to receive real-time progress (follows loading, summary generation). See Real-time Updates (SSE).

  • Report page (current default): GET /report/:reportId returns a slim ReportPage (per-source rows, previews, merged highlights; optional ?sortByRedFlags=true&limit=…, If-None-Match for ETag / 304). This replaces the old monolithic “full report in one JSON” for new clients.
  • Full interest trees (lazy detail): GET /report/:reportId/interests — full category trees for the interests section; optional ?source=ig|tw|… to fetch one network only.
  • Full monolithic payload: GET /report/:reportId/full — full ReportItem data per profile (optional embedded summary). Prefer GET /report/:reportId + GET /report/:reportId/summary for new integrations.
  • Per source: GET /report/:reportId/:source.
  • Friends (unified): GET /report/:reportId/friends?limit=…&offset=… — merged list across connected sources (sortByRedFlags supported).
  • Friends (per network): GET /report/:reportId/friends/:source?limit=…&offset=….
  • Common interests with a friend: GET /report/:reportId/:source/common/:friendId.
  • Interest details: GET /report/:reportId/:source/:interestId and GET /report/:reportId/:source/:interestId/profiles.
  • Interest changes over time: GET /report/:reportId/:source/diffs?date=2024-01&type=new.
  • AI summary: GET /report/:reportId/summary (may be generated asynchronously; use SSE or poll /check until generated is true).

Reports can be fetched later by report ID without re-running the flow; store the ID if you need to access the same report again.


FAQ​

I get 403 "access denied" on GET /create/:reportId (step 3). What am I doing wrong?​

You must send the Bearer token on every request after sign-in, including GET requests. Add the header: Authorization: Bearer <your_token>. Steps 1 and 2 often work because signin returns the token and create-report is a POST that developers tend to send with auth; the GET to retrieve profiles is frequently called without the header, which causes 403.

Use this order: (1) POST /auth/signin → (2) POST /create (with Bearer) → (3) GET /create/:reportId (with Bearer) → (4) POST /create/:reportId/:source/add for each profile (with Bearer) → (5) POST /create/:reportId/confirm (with Bearer) → (6) wait via /check or /sse → (7) GET /report/:reportId (with Bearer). See Quick Start and Report Pipeline for details and options.

Can I change the report after I confirmed?​

No. After POST /create/:reportId/confirm the selection is locked. To change profiles you must create a new report (new POST /create) and go through the flow again. Calling confirm again on the same report returns 409 ("report profiles were already confirmed") rather than re-locking it.

Why is a profile not available or marked with badReason?​

Common reasons: private (account is private), notFound (username does not exist), zeroFollows (profile follows no one, so we cannot analyze interests), optOut (user requested data removal), unloaded (data not loaded yet), unmapped (profile could not be mapped), unexpected (an unexpected failure). Use only profiles without a blocking badReason for add/confirm.

What if loading fails for an account after I confirmed?​

After confirm, the API loads each profile and its subscriptions (follows). If loading fails for one profile (e.g. timeout, account private, or follows unavailable), you get a per-profile error, not an HTTP error. Subscribe to GET /sse/:reportId and handle follows events with status: "error" and the error field (e.g. private, unloaded). When polling GET /check/:reportId, check loadError and errorSource. The report may still contain data for other profiles; the failed one will have no follows/interests. See Error Handling — Profile Loading Errors for details.

How do I know when the report is ready to fetch?​

Call GET /check/:reportId. When ready === true the main report data is available; when generated === true the full report including AI summary is ready. Alternatively use SSE GET /sse/:reportId for real-time events (follows done, summary done). GET /check/:reportId returns 404 (not 403) when the caller does not own the report, to avoid revealing that a given report ID exists.

What are the source codes (ig, tw, fb, tt, li)?​

They are platform identifiers: ig = Instagram, tw = Twitter, fb = Facebook, tt = TikTok, li = LinkedIn. Use them in POST /create (field source), in paths like /create/:reportId/ig/add, and in the confirm payload.

Does the report ID expire?​

Report IDs are persistent. You can store them and fetch the same report later with GET /report/:reportId as long as you have a valid token and the report belongs to your account.

I got a 402. What does that mean?​

402 comes from POST /create (report creation), not from confirm: your account has no remaining report credits (message: "to continue please upgrade your subscription", or "You have reached search limit. Please Sign up to continue." for an unauthenticated caller). If you have credits at creation time but run out before you view the finished report, the report page shows blurred/placeholder data instead of an error — check your subscription or purchase more reports in the dashboard.

Can I add several profiles from different networks to one report?​

Yes. Add each profile with POST /create/:reportId/:source/add (one request per profile; use the correct source for each). Then confirm with an array that includes all selected profiles with their source and id.

Can I send only a list of accounts in confirm without calling add first?​

No. Confirm does not add profiles to the report. It only locks the selection: the body must list exactly the profiles already added via POST /create/:reportId/:source/add. The server checks that each source and id in the confirm body matches the profile currently stored for that source; otherwise it returns an error. Always add each profile first, then call confirm with that same list.

Where do I get the profile id to pass to add/confirm?​

From the response of GET /create/:reportId or GET /create/:reportId/:source. Each item in the profiles array (or per-source list) has an id and a source. Use those same values in add and confirm.