> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neuro-tech.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Discover your first report

> Call the login, presence, and query endpoints over HTTPS to discover report IDs and retrieve their parameter forms.

Sign in, resolve your report server, and retrieve a report's parameter form using ordinary HTTPS requests.
You can follow this guide with any HTTP client or backend language.

<Note>
  For a dashboard, execute reports from a scheduled backend job or a controlled manual refresh and cache the completed result.
  Dashboard loads should read that cache. See [Refresh reports for a dashboard](/reports/dashboard-integration).
</Note>

## Prepare your connection

Have these values ready before sending requests:

| Value                 | Example                      | Where it is used                                                                           |
| --------------------- | ---------------------------- | ------------------------------------------------------------------------------------------ |
| Neuron host           | `neuron.example.com`         | HTTPS destination and host in the login signature. Obtain it from your provider.           |
| Username and password | Supplied by your provider    | Sign the login request for the account with report access.                                 |
| Application URL       | `https://your-app.example/`  | `Referer` header on every request, including backend calls.                                |
| Reports JID           | `reports@neuron.example.com` | Report server address supplied by your provider; separate from the HTTP host and username. |

The provider must grant report access and approve your account's presence subscription to the report server.
Keep shared credentials and JWTs on your backend. All hosts, report names, and response values below are illustrative.

The request destination is always your Agent API host, even when the Reports JID points to another Neuron.
For example, the query URL is `https://neuron.example.com/Agent/Xmpp/InformationQuery`.
The `to` field inside its JSON body selects the report server.

## 1. Sign in

Generate a fresh nonce from at least 32 cryptographically random bytes and encode it as Base64.
Calculate the signature using the exact username, hostname, and nonce:

```text theme={null}
message = userName + ":" + host + ":" + nonce
signature = Base64(HMAC-SHA256(key=UTF8(password), data=UTF8(message)))
```

The host excludes `https://` and paths. Include a non-default port if your provider uses one.
Use standard Base64 for the HMAC digest. Send the same nonce string that you signed.

Send `POST https://neuron.example.com/Agent/Account/Login`:

```http theme={null}
POST /Agent/Account/Login HTTP/1.1
Host: neuron.example.com
Referer: https://your-app.example/
Content-Type: application/json
Accept: application/json

{
  "userName": "<USERNAME>",
  "nonce": "<FRESH_NONCE>",
  "signature": "<CALCULATED_SIGNATURE>",
  "seconds": 3600
}
```

Login does not require a bearer token. A successful response contains:

```json theme={null}
{
  "jwt": "<JWT>",
  "expires": 1788958800
}
```

Retain the returned `jwt` and expiry in your backend session. Use that JWT in all remaining requests.
`expires` is a Unix timestamp in seconds; multiply by 1,000 before passing it to JavaScript's `Date` constructor.
Replace the example `Referer` with your application's HTTPS URL throughout this guide.

## 2. Resolve the report server

Send the provider's bare Reports JID to `POST https://neuron.example.com/Agent/Xmpp/PresenceProbe`:

```http theme={null}
POST /Agent/Xmpp/PresenceProbe HTTP/1.1
Host: neuron.example.com
Referer: https://your-app.example/
Authorization: Bearer <JWT>
Content-Type: application/json
Accept: application/json

{ "to": "reports@neuron.example.com" }
```

On success, read `jid`. An illustrative response, with other fields omitted:

```json theme={null}
{
  "jid": "reports@neuron.example.com/current-resource",
  "type": "available"
}
```

Use this full JID, including `/current-resource`, as `to` in subsequent queries. Do not invent the resource suffix.
If the response is unavailable or lacks a full JID, wait for the target to become available and resolve it again.
The suffix can change when the target reconnects. If your provider already supplied a current full JID, use it directly and skip the probe.

A probe can send a presence subscription request and return `403` until the target approves it.
Ask your provider to approve that request before retrying; a successful login does not grant presence access.

## 3. Discover report IDs

Send `POST https://neuron.example.com/Agent/Xmpp/InformationQuery` with `type: "get"` and the `getAllNodes` XML command:

```http theme={null}
POST /Agent/Xmpp/InformationQuery HTTP/1.1
Host: neuron.example.com
Referer: https://your-app.example/
Authorization: Bearer <JWT>
Content-Type: application/json
Accept: application/json

{
  "to": "reports@neuron.example.com/current-resource",
  "type": "get",
  "xml": "<getAllNodes xmlns='urn:nf:iot:concentrator:1.0' xml:lang='en' src='Reports'/>"
}
```

The outer HTTP method remains `POST`. The JSON `type` selects the query operation, and `xml` is a string containing one XML element.
Keep the namespace and source ID `Reports` exactly as shown.

Check both the HTTP status and `ok` in the response. A successful response can look like this:

```json theme={null}
{
  "ok": true,
  "Stanza": {
    "nodeInfos": {
      "nodeInfo": {
        "id": "Client Reports\\Monthly usage.rpx",
        "parentId": "Client Reports",
        "hasCommands": "true",
        "hasChildren": "false"
      }
    }
  }
}
```

Read `Stanza.nodeInfos.nodeInfo`, which can be one object, an array, or absent when there are no nodes.
To obtain the same report list as the Reports helper, keep nodes with a `parentId` and enabled `hasCommands`, excluding nodes with `hasChildren` enabled.
These flags can be JSON booleans or strings. Preserve each matching `id` exactly, including spaces and backslashes.

An empty list means no matching report nodes were returned. An HTTP error or `ok: false` means the query failed.
If the source is missing or hidden, use the [source visibility check](/reports/troubleshooting#check-source-visibility) before continuing.

## 4. Fetch the selected report's parameters

Call the **same endpoint** with `getCommandParameters`, the discovered report ID, and `command='Execute'`:

```http theme={null}
POST /Agent/Xmpp/InformationQuery HTTP/1.1
Host: neuron.example.com
Referer: https://your-app.example/
Authorization: Bearer <JWT>
Content-Type: application/json
Accept: application/json

{
  "to": "reports@neuron.example.com/current-resource",
  "type": "get",
  "xml": "<getCommandParameters xmlns='urn:nf:iot:concentrator:1.0' xml:lang='en' src='Reports' id='Client Reports\\Monthly usage.rpx' command='Execute'/>"
}
```

After checking `ok: true`, read the form from `Stanza.x`. An illustrative response, with metadata omitted:

```json theme={null}
{
  "ok": true,
  "Stanza": {
    "x": {
      "type": "form",
      "field": {
        "var": "Month",
        "type": "text-single",
        "label": "Month",
        "required": {},
        "value": { "value": "2026-08" }
      }
    }
  }
}
```

Use the returned field names, types, defaults, allowed values, and validation rules.
`Month` is an example; your report may have different fields or none. Preserve hidden fields and normalize a single `field` object to an array.
See [parameter forms](/reports/responses#parameter-forms) for multi-value fields and other types.

Use an XML builder or escape XML attributes when substituting report IDs. Then use a JSON serializer for the outer request.
In JSON, `\\` represents one backslash; the report ID itself contains a single backslash.

## 5. Execute or end the session

You now have the JWT, full Reports JID, exact report ID, and parameter form needed to execute a report.
Keep this session open and continue with [Run a report over HTTP](/reports/execute-over-http), which shows submission and result collection.

If you are finished with discovery, send `POST https://neuron.example.com/Agent/Account/Logout` with an empty JSON object:

```http theme={null}
POST /Agent/Account/Logout HTTP/1.1
Host: neuron.example.com
Referer: https://your-app.example/
Authorization: Bearer <JWT>
Content-Type: application/json
Accept: application/json

{}
```

Discard the JWT after logout. Sign in again before starting a new execution session.

## Optional Node.js sample

The [discovery sample](/downloads/reports-discovery.mjs) implements login, presence resolution, report listing, parameter retrieval, and logout.
It requires Node.js 22 or later and does not execute reports. Save it as `discover.mjs` and create a private `.env` file beside it:

```dotenv theme={null}
NEURON_HOST=neuron.example.com
NEURON_USER=client.reports
NEURON_PASSWORD="replace-with-the-provided-password"
NEURON_REFERER=https://your-app.example/
REPORTS_JID=reports@neuron.example.com
REPORT_LANGUAGE=en
```

Run these commands separately to list IDs and fetch one report's form:

```bash theme={null}
node --env-file=.env discover.mjs
node --env-file=.env discover.mjs "Client Reports\Monthly usage.rpx"
```

Each invocation signs in and logs out. Keep `.env` out of source control.
