---
cuppy_hub: sap
cuppy_path: reference/connectivity/odata-calls-csrf-and-errors.md
cuppy_section: docs
cuppy_doc: E6MH-AWJQ
cuppy_doc_uuid: f8f0999e-7532-4e0d-8592-357308500f4c
cuppy_permalink: https://cuppy.io/sap/d/E6MH-AWJQ
cuppy_version: 1
cuppy_updated: 2026-10-23T16:16:52.575Z
cuppy_canonical: https://cuppy.io/sap/docs/reference/connectivity/odata-calls-csrf-and-errors
cuppy_mcp: https://cuppy.io/mcp
cuppy_body_sha256: 1f73d5dbb65825106617d8fd70a983cafb6b8f590358c00b8486ee2a403d341e
# cuppy_body_sha256 is over the document body ALONE, excluding both generated blocks.
---


# Calling SAP OData services

## URL anatomy

```text
https://<host>[:port]/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=5&$select=BusinessPartner,BusinessPartnerFullName&$format=json
           \ system  / \ Gateway OData root /  \ service   /  \ entity set  /  \ query options                                          /
```

- **Service and entity names** come from the API's page on the Business Accelerator Hub. Fetch `…/<SERVICE>/$metadata` to see entity sets, keys and fields.
- **V2 query options:** `$filter`, `$select`, `$expand`, `$orderby`, `$top`, `$skip` and `$inlinecount`. `$format=json` returns JSON with results under `d.results`. V4 services follow OData V4 (results under `value`).
- **`sap-client`:** pick the tenant (client) on systems with more than one, as a query parameter or header. A destination's `sap-client` property makes the SAP Cloud SDK send it.

The hub's reading: the URL root, `d.results` and the query options are the standard SAP Gateway OData conventions. Family 3 will cite each published API's own specification.

## Writes need a CSRF token

SAP Gateway rejects a modifying request (POST, PUT, PATCH, DELETE) that lacks a valid `X-CSRF-Token`. The handshake, exactly as the SAP Cloud SDK performs it:

```text
HEAD /resource/path/        X-CSRF-Token: fetch   → token returned? use it
HEAD /resource/path         X-CSRF-Token: fetch   → token returned? use it, otherwise send without one
then: POST /resource/path   X-CSRF-Token: <token> + the cookies the HEAD set
```

- **Keep the session cookies.** The token belongs to the session the `HEAD` started; the SDK reuses its `Set-Cookie` values. A token sent without its cookies fails.
- **Default behaviour:** the SDK fetches a token for every non-GET (`fetchCsrfToken: true`), and never for GET.
- **Turning it off:** set `fetchCsrfToken: false` only for targets that don't use CSRF, such as many non-SAP APIs.

## Updates, batches and paging

- **ETags:** entities with an ETag must be updated with `If-Match: <etag>` from the last read. A missing or stale value is rejected (412).
- **`$batch`:** several operations in one multipart request. Writes inside the batch share one CSRF token.
- **Paging:**
  - page with `$top` and `$skip`, or follow the server's next link (`__next` in V2 JSON, `@odata.nextLink` in V4);
  - never assume one call returns everything.

## What failures usually mean

The hub's reading of the usual causes; family 2's error catalogue will add SAP's verbatim messages.

| Status | Usual cause → fix |
|---|---|
| 401 | Wrong user or password, an expired OAuth token, or a communication user not in the arrangement |
| 403 on a write, with a CSRF-related message or header | Missing, expired or session-less CSRF token. Fetch it with `X-CSRF-Token: fetch`, and send it with the same cookies |
| 403 on a read | The user lacks the role or authorization for that service or object |
| 404 for the whole service | The service isn't activated on the system, or the Cloud Connector doesn't expose that path |
| 405 | The entity set doesn't allow that method. Check the API specification |
| 412 | ETag mismatch. Re-read the entity and resend `If-Match` |
| 400 with an OData error body | Bad `$filter` syntax, or a wrong field name or type. Read `error.message.value` (V2) or `error.message` (V4) |


---
<!-- cuppy -->
You are reading one file. The same content is free over MCP at https://cuppy.io/mcp with no signup: ranked search across all 10 documents in this hub, the merged view when you work in a layer, known workarounds matched to the error you hit, and depth control so you spend tokens on what you need. Start with the `_hello` tool. How to connect: https://cuppy.io/sap/ Something wrong in this document? Report it with the `session feedback` tool on the same endpoint.
<!-- /cuppy -->
