> For the complete documentation index, see [llms.txt](https://cortex-docs.paloaltonetworks.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cortex-docs.paloaltonetworks.com/xdr-3-api/broker-vm-tenant-side/remote-log-bundle.md).

# Remote log bundle

Cloud-orchestrated three-step flow to obtain a log bundle from a remote broker: request generation, poll for completion, then download. For an on-appliance synchronous stream, use the `/public_api/v1/logs` endpoint of the direct Broker API spec.

## Request asynchronous log-bundle collection from a broker

> Ask the broker to collect a log bundle and upload it to cloud\
> storage. The operation is \*\*idempotent\*\*: if a previous bundle\
> request for the same broker is still in flight, this call returns\
> \`LogRequestAlreadyInProgress\` rather than enqueuing a second job.\
> \
> Once enqueued, poll\
> \[\`getLogBundleStatus\`]\(#operation/getLogBundleStatus) until\
> \`LogRequestSucceeded\`, then download via\
> \[\`downloadLogBundle\`]\(#operation/downloadLogBundle).\
> \
> \*\*Required permission:\*\* \`broker\_action\`.<br>

```json
{"openapi":"3.0.3","info":{"title":"Cortex Broker Public API (tenant-side)","version":"1.0.0"},"tags":[{"name":"logs","description":"Cloud-orchestrated three-step flow to obtain a log bundle from a\nremote broker: request generation, poll for completion, then\ndownload. For an on-appliance synchronous stream, use the\n`/public_api/v1/logs` endpoint of the direct Broker API spec.\n"}],"servers":[{"url":"https://api-{fqdn}","description":"Cortex tenant base URL","variables":{"fqdn":{"default":"cortex.paloaltonetworks.com","description":"Fully qualified domain name of the Cortex tenant."}}}],"security":[{"XDRAuth":[],"XDRAuthToken":[]}],"components":{"securitySchemes":{"XDRAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"The tenant API key value, sent as the literal `Authorization`\nheader value (no `Bearer ` prefix).\n"}},"parameters":{"DeviceIdPath":{"name":"device_id","in":"path","required":true,"description":"The broker device identifier as returned by `getBrokers`.","schema":{"type":"string","minLength":1}}},"schemas":{"LogBundleGenerateResponse":{"description":"Payload returned by `generateLogBundle`. The `code` field discriminates between newly-enqueued (`LogRequestSubmitted`) and already-in-flight (`LogRequestAlreadyInProgress`).\n","type":"object","required":["code","status"],"properties":{"code":{"type":"string","enum":["LogRequestSubmitted","LogRequestAlreadyInProgress"],"description":"`LogRequestSubmitted` — a new bundle request was enqueued.\n`LogRequestAlreadyInProgress` — a prior request is still in\nflight and was not duplicated.\n"},"status":{"type":"boolean","description":"Always `true` for this endpoint; reserved for parity with the legacy log-status payload."}}},"PapiErrorResponse":{"type":"object","description":"Canonical error envelope returned by every non-2xx response on\nthis surface.\n","required":["description"],"properties":{"description":{"type":"string","description":"Human-readable error message."},"errors":{"type":"array","description":"Per-field validation failures. Present only on\n`400 Bad Request` responses originating from Pydantic schema\nvalidation; otherwise omitted.\n","items":{"$ref":"#/components/schemas/PapiFieldError"}}}},"PapiFieldError":{"description":"One entry in the `errors` array of a `400 Bad Request` response — a Pydantic per-field validation failure.\n","type":"object","required":["field","message"],"properties":{"field":{"type":"string","description":"Dotted path to the offending field within the request body,\nusing ` -> ` as separator for nested keys\n(e.g., `proxy -> type`, `upgrade_window -> days -> 0`).\n"},"message":{"type":"string","description":"Pydantic validation message."}}}},"responses":{"Unauthorized":{"description":"Missing or invalid `Authorization` / `x-xdr-auth-id` credentials.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"Forbidden":{"description":"Authenticated caller lacks the `broker_action` permission, or the\ntargeted broker is a cluster member and the operation is rejected\nat that level.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"NotFound":{"description":"The targeted resource does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"Conflict":{"description":"The action cannot proceed in the current state — most commonly,\nthe broker is disconnected, or an applet is not in the required\nactive/inactive state.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"InternalServerError":{"description":"Unexpected server-side failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}}}},"paths":{"/public_api/v1/brokers/{device_id}/logs/generate/":{"post":{"tags":["logs"],"operationId":"generateLogBundle","summary":"Request asynchronous log-bundle collection from a broker","description":"Ask the broker to collect a log bundle and upload it to cloud\nstorage. The operation is **idempotent**: if a previous bundle\nrequest for the same broker is still in flight, this call returns\n`LogRequestAlreadyInProgress` rather than enqueuing a second job.\n\nOnce enqueued, poll\n[`getLogBundleStatus`](#operation/getLogBundleStatus) until\n`LogRequestSucceeded`, then download via\n[`downloadLogBundle`](#operation/downloadLogBundle).\n\n**Required permission:** `broker_action`.\n","parameters":[{"$ref":"#/components/parameters/DeviceIdPath"}],"responses":{"200":{"description":"Request enqueued (or already in progress).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogBundleGenerateResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalServerError"}}}}}}
```

## Poll the status of a log-bundle request

> Return the status of the most recent log-bundle request for the\
> broker. The \`log\_collection\_time\` field is always present (ISO 8601\
> UTC string when the bundle has been collected, \`null\` otherwise) so\
> clients can rely on a stable response shape regardless of state.\
> \
> See the \[Broker Log Bundle Status Codes]\(<https://app.gitbook.com/s/bcaz3nnErYwzhJKuv5Ls/broker-vm-tenant-side/broker-papi-tables#broker-log-bundle-status-codes>) table.\
> \
> Use \`code\` (not \`status\`) to distinguish states: \`status\` is \`true\` only for \`LogRequestSucceeded\`.\
> \
> \*\*Required permission:\*\* \`broker\_action\`.<br>

```json
{"openapi":"3.0.3","info":{"title":"Cortex Broker Public API (tenant-side)","version":"1.0.0"},"tags":[{"name":"logs","description":"Cloud-orchestrated three-step flow to obtain a log bundle from a\nremote broker: request generation, poll for completion, then\ndownload. For an on-appliance synchronous stream, use the\n`/public_api/v1/logs` endpoint of the direct Broker API spec.\n"}],"servers":[{"url":"https://api-{fqdn}","description":"Cortex tenant base URL","variables":{"fqdn":{"default":"cortex.paloaltonetworks.com","description":"Fully qualified domain name of the Cortex tenant."}}}],"security":[{"XDRAuth":[],"XDRAuthToken":[]}],"components":{"securitySchemes":{"XDRAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"The tenant API key value, sent as the literal `Authorization`\nheader value (no `Bearer ` prefix).\n"}},"parameters":{"DeviceIdPath":{"name":"device_id","in":"path","required":true,"description":"The broker device identifier as returned by `getBrokers`.","schema":{"type":"string","minLength":1}}},"schemas":{"LogBundleStatusResponse":{"description":"Payload returned by `getLogBundleStatus`. The `code` field reflects the current state of the log bundle.\n","type":"object","required":["code","status","log_collection_time"],"properties":{"code":{"type":"string","enum":["LogRequestInProgress","LogRequestSucceeded","LogRequestTimedout","LogBundleCollectionInVMFailed"],"description":"Status of the most recent log-bundle request. See the\nendpoint description for the meaning of each value.\n"},"status":{"type":"boolean","description":"`true` only when the bundle is ready for download\n(`LogRequestSucceeded`); `false` for in-progress and all\nfailure states. Key off `code` rather than `status` to\ndistinguish in-progress from failure.\n"},"log_collection_time":{"type":"string","nullable":true,"description":"ISO 8601 UTC timestamp of when the broker finished\ncollecting the bundle. `null` when the bundle has not yet\ncompleted (or the request failed before completion).\n"}}},"PapiErrorResponse":{"type":"object","description":"Canonical error envelope returned by every non-2xx response on\nthis surface.\n","required":["description"],"properties":{"description":{"type":"string","description":"Human-readable error message."},"errors":{"type":"array","description":"Per-field validation failures. Present only on\n`400 Bad Request` responses originating from Pydantic schema\nvalidation; otherwise omitted.\n","items":{"$ref":"#/components/schemas/PapiFieldError"}}}},"PapiFieldError":{"description":"One entry in the `errors` array of a `400 Bad Request` response — a Pydantic per-field validation failure.\n","type":"object","required":["field","message"],"properties":{"field":{"type":"string","description":"Dotted path to the offending field within the request body,\nusing ` -> ` as separator for nested keys\n(e.g., `proxy -> type`, `upgrade_window -> days -> 0`).\n"},"message":{"type":"string","description":"Pydantic validation message."}}}},"responses":{"Unauthorized":{"description":"Missing or invalid `Authorization` / `x-xdr-auth-id` credentials.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"Forbidden":{"description":"Authenticated caller lacks the `broker_action` permission, or the\ntargeted broker is a cluster member and the operation is rejected\nat that level.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"InternalServerError":{"description":"Unexpected server-side failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}}}},"paths":{"/public_api/v1/brokers/{device_id}/logs/status/":{"get":{"tags":["logs"],"operationId":"getLogBundleStatus","summary":"Poll the status of a log-bundle request","description":"Return the status of the most recent log-bundle request for the\nbroker. The `log_collection_time` field is always present (ISO 8601\nUTC string when the bundle has been collected, `null` otherwise) so\nclients can rely on a stable response shape regardless of state.\n\nSee the [Broker Log Bundle Status Codes](https://app.gitbook.com/s/bcaz3nnErYwzhJKuv5Ls/broker-vm-tenant-side/broker-papi-tables#broker-log-bundle-status-codes) table.\n\nUse `code` (not `status`) to distinguish states: `status` is `true` only for `LogRequestSucceeded`.\n\n**Required permission:** `broker_action`.\n","parameters":[{"$ref":"#/components/parameters/DeviceIdPath"}],"responses":{"200":{"description":"Current log-bundle request status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LogBundleStatusResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"500":{"$ref":"#/components/responses/InternalServerError"}}}}}}
```

## Download the most recent log bundle for a broker

> Stream the most recent log bundle for the broker as a gzipped tar\
> archive (\`\<device\_id>\_\<epoch\_ms>.tar.gz\`). Call only after\
> \[\`getLogBundleStatus\`]\(#operation/getLogBundleStatus) returns\
> \`LogRequestSucceeded\`; calling earlier yields \`4xx\`.\
> \
> \*\*Required permission:\*\* \`broker\_action\`.<br>

```json
{"openapi":"3.0.3","info":{"title":"Cortex Broker Public API (tenant-side)","version":"1.0.0"},"tags":[{"name":"logs","description":"Cloud-orchestrated three-step flow to obtain a log bundle from a\nremote broker: request generation, poll for completion, then\ndownload. For an on-appliance synchronous stream, use the\n`/public_api/v1/logs` endpoint of the direct Broker API spec.\n"}],"servers":[{"url":"https://api-{fqdn}","description":"Cortex tenant base URL","variables":{"fqdn":{"default":"cortex.paloaltonetworks.com","description":"Fully qualified domain name of the Cortex tenant."}}}],"security":[{"XDRAuth":[],"XDRAuthToken":[]}],"components":{"securitySchemes":{"XDRAuth":{"type":"apiKey","in":"header","name":"Authorization","description":"The tenant API key value, sent as the literal `Authorization`\nheader value (no `Bearer ` prefix).\n"}},"parameters":{"DeviceIdPath":{"name":"device_id","in":"path","required":true,"description":"The broker device identifier as returned by `getBrokers`.","schema":{"type":"string","minLength":1}}},"responses":{"Unauthorized":{"description":"Missing or invalid `Authorization` / `x-xdr-auth-id` credentials.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"Forbidden":{"description":"Authenticated caller lacks the `broker_action` permission, or the\ntargeted broker is a cluster member and the operation is rejected\nat that level.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"NotFound":{"description":"The targeted resource does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"Conflict":{"description":"The action cannot proceed in the current state — most commonly,\nthe broker is disconnected, or an applet is not in the required\nactive/inactive state.\n","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}},"InternalServerError":{"description":"Unexpected server-side failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PapiErrorResponse"}}}}},"schemas":{"PapiErrorResponse":{"type":"object","description":"Canonical error envelope returned by every non-2xx response on\nthis surface.\n","required":["description"],"properties":{"description":{"type":"string","description":"Human-readable error message."},"errors":{"type":"array","description":"Per-field validation failures. Present only on\n`400 Bad Request` responses originating from Pydantic schema\nvalidation; otherwise omitted.\n","items":{"$ref":"#/components/schemas/PapiFieldError"}}}},"PapiFieldError":{"description":"One entry in the `errors` array of a `400 Bad Request` response — a Pydantic per-field validation failure.\n","type":"object","required":["field","message"],"properties":{"field":{"type":"string","description":"Dotted path to the offending field within the request body,\nusing ` -> ` as separator for nested keys\n(e.g., `proxy -> type`, `upgrade_window -> days -> 0`).\n"},"message":{"type":"string","description":"Pydantic validation message."}}}}},"paths":{"/public_api/v1/brokers/{device_id}/logs/download/":{"get":{"tags":["logs"],"operationId":"downloadLogBundle","summary":"Download the most recent log bundle for a broker","description":"Stream the most recent log bundle for the broker as a gzipped tar\narchive (`<device_id>_<epoch_ms>.tar.gz`). Call only after\n[`getLogBundleStatus`](#operation/getLogBundleStatus) returns\n`LogRequestSucceeded`; calling earlier yields `4xx`.\n\n**Required permission:** `broker_action`.\n","parameters":[{"$ref":"#/components/parameters/DeviceIdPath"}],"responses":{"200":{"description":"Gzipped tar archive of the log bundle.","headers":{"Content-Disposition":{"description":"Attachment filename, `<device_id>_<epoch_ms>.tar.gz`.","schema":{"type":"string"}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary","description":"Raw bytes of the gzipped tar archive."}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"500":{"$ref":"#/components/responses/InternalServerError"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://cortex-docs.paloaltonetworks.com/xdr-3-api/broker-vm-tenant-side/remote-log-bundle.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
