{
  "info": {
    "name": "valueverde Partner API (v1.6.0)",
    "description": "Generated from the published OpenAPI document at https://docs.valueverde.de/spec/partner-api.yaml — do not edit by hand, it is regenerated on every docs build.\n\n**To use it:** set `clientId` and `clientSecret` in the collection variables, send \"0. Get an access token\", then work down the folders. Ids are captured into variables as you go, so a top-to-bottom run needs no copy-paste.\n\nIt points at **staging** by default. Change `baseUrl` to `https://api.valueverde.de` when you go live.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{accessToken}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.staging.valueverde.de",
      "description": "Staging by default. Swap to https://api.valueverde.de when you go live.",
      "type": "string"
    },
    {
      "key": "clientId",
      "value": "",
      "description": "Your OAuth2 client id, from partner support.",
      "type": "string"
    },
    {
      "key": "clientSecret",
      "value": "",
      "description": "Your OAuth2 client secret.",
      "type": "string"
    },
    {
      "key": "accessToken",
      "value": "",
      "description": "Captured automatically by \"Get an access token\". Do not set by hand.",
      "type": "string"
    },
    {
      "key": "investorId",
      "value": "",
      "description": "Captured from the first investor you provision or read.",
      "type": "string"
    },
    {
      "key": "cooperativeId",
      "value": "",
      "description": "Captured from the first cooperative listed.",
      "type": "string"
    },
    {
      "key": "sharePurchaseId",
      "value": "",
      "description": "Captured from the first order you place.",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "0. Get an access token",
      "event": [
        {
          "listen": "test",
          "script": {
            "type": "text/javascript",
            "exec": [
              "// Every other request in this collection sends {{accessToken}}.",
              "// Run this one first; re-run it when the token expires (15 minutes).",
              "if (pm.response.code === 200) {",
              "    pm.collectionVariables.set(\"accessToken\", pm.response.json().access_token);",
              "}"
            ]
          }
        }
      ],
      "request": {
        "auth": {
          "type": "basic",
          "basic": [
            {
              "key": "username",
              "value": "{{clientId}}",
              "type": "string"
            },
            {
              "key": "password",
              "value": "{{clientSecret}}",
              "type": "string"
            }
          ]
        },
        "method": "POST",
        "header": [
          {
            "key": "Content-Type",
            "value": "application/x-www-form-urlencoded"
          }
        ],
        "body": {
          "mode": "urlencoded",
          "urlencoded": [
            {
              "key": "grant_type",
              "value": "client_credentials",
              "type": "text"
            }
          ]
        },
        "url": {
          "raw": "{{baseUrl}}/oauth2/token",
          "host": [
            "{{baseUrl}}"
          ],
          "path": [
            "oauth2",
            "token"
          ]
        },
        "description": "Exchanges your client credentials for a 15-minute access token and stores it in the {{accessToken}} collection variable.\n\nCache the token — the endpoint allows 20 requests a minute, which an integration that mints one per call will exhaust immediately. See https://docs.valueverde.de/docs/rate-limits."
      }
    },
    {
      "name": "Client",
      "description": "Identity and granted scopes of the OAuth2 client the request is authenticated as.",
      "item": [
        {
          "name": "Describe the calling OAuth2 client",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/me",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "me"
              ]
            },
            "description": "Returns the client_id, the organisation the client acts for, the scopes carried by the presented token, and the full set the client was granted — the set you may request at the token endpoint. Requires no scope beyond a valid partner token. `token_scopes` is a subset of `granted_scopes`; they differ only when the token request asked for less than everything.\n\n**Errors:** 401, 403, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        }
      ]
    },
    {
      "name": "Cooperatives (partner)",
      "description": "Versioned partner catalogue of published cooperatives. Narrower than the internal /cooperatives contract by design; responses are conditional-GET friendly.",
      "item": [
        {
          "name": "List published cooperatives (partner)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "If-None-Match",
                "value": "",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/cooperatives",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "cooperatives"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "size",
                  "value": "20",
                  "disabled": true
                },
                {
                  "key": "sort",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "q",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "city",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "bafa_funded",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "dividend_type",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "min_share_price",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "max_share_price",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "affiliation",
                  "value": "",
                  "disabled": true
                },
                {
                  "key": "near_lat",
                  "value": "",
                  "description": "Latitude of a geo search. Send with near_lng; sorts by distance and applies no other filter.",
                  "disabled": true
                },
                {
                  "key": "near_lng",
                  "value": "",
                  "description": "Longitude of a geo search. Send with near_lat.",
                  "disabled": true
                },
                {
                  "key": "radius_km",
                  "value": "",
                  "description": "Search radius in kilometres. Only valid with near_lat/near_lng.",
                  "disabled": true
                },
                {
                  "key": "include_coordinates",
                  "value": "false",
                  "disabled": true
                }
              ]
            },
            "description": "Paginated, filtered, sorted catalogue card. page is one-based; size is capped at 100. Sort tokens: created_at_desc (default), created_at_asc, name_asc, name_desc, share_price_asc, share_price_desc. Min/max share-price filters are interpreted as EUR. Pass include_coordinates=true to add latitude/longitude for map rendering. Send the returned ETag back as If-None-Match to get a 304.\n\n**Scopes:** `cooperatives:read`\n\n**Errors:** 400, 401, 403, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (body.items && body.items.length) {",
                  "    pm.collectionVariables.set(\"cooperativeId\", body.items[0].cooperative_id);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "Get a single published cooperative (partner)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "If-None-Match",
                "value": "",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/cooperatives/{{cooperativeId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "cooperatives",
                "{{cooperativeId}}"
              ],
              "query": [
                {
                  "key": "expand",
                  "value": "",
                  "disabled": true
                }
              ]
            },
            "description": "Returns the partner record, with projects rolled up into projects_by_type. Pass expand=projects to additionally receive the individual projects. Unpublished cooperatives return 404 — publication state is not observable here. Send the returned ETag back as If-None-Match to get a 304.\n\n**Scopes:** `cooperatives:read`, `projects:read`\n\n**Errors:** 400, 401, 403, 404, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        }
      ]
    },
    {
      "name": "Managed Investors",
      "description": "B2B surface for partners to provision and manage the investors they act on behalf of.",
      "item": [
        {
          "name": "Provision a managed investor",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/investors",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors"
              ]
            },
            "description": "Creates a party identity (investor account + profile) the partner owns. Supplying the optional `profile` block completes the investor in the same call, which is the normal case. Idempotent on partner_customer_ref: re-provisioning the same ref with the same details replays the existing investor (200) instead of creating a second one (201); re-provisioning it with a different inline profile is a 409 — use PUT /v1/investors/{id}/profile to change a profile. The investor has no login user and cannot authenticate directly.\n\n**Scopes:** `investors:write`\n\n**Errors:** 400, 401, 403, 422, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"partner_customer_ref\": \"nb-cust-001\",\n  \"email\": \"maria@example.org\",\n  \"profile\": {\n    \"first_name\": \"Maria\",\n    \"last_name\": \"Santos\",\n    \"birth_date\": \"1985-03-15\",\n    \"tax_id\": \"DE12345678901\",\n    \"email\": \"maria@example.org\",\n    \"phone\": \"+49 30 1234567\",\n    \"address\": {\n      \"street\": \"Hauptstrasse\",\n      \"house_number\": \"1\",\n      \"postal_code\": \"10115\",\n      \"city\": \"Berlin\"\n    },\n    \"bank_account\": {\n      \"iban\": \"DE89370400440532013000\",\n      \"bic\": \"COBADEFFXXX\",\n      \"institution\": \"Commerzbank\",\n      \"account_holder\": \"Maria Santos\"\n    },\n    \"company\": {\n      \"name\": \"Solar Nord GmbH\",\n      \"legal_form\": \"GmbH\",\n      \"tax_id\": \"DE99999999999\"\n    },\n    \"consents\": {\n      \"terms\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      },\n      \"privacy\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      },\n      \"data_sharing\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      },\n      \"representation_authorization\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      }\n    }\n  }\n}\n",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (body.investor_id) pm.collectionVariables.set(\"investorId\", body.investor_id);"
                ]
              }
            }
          ]
        },
        {
          "name": "Find a managed investor by your own customer reference",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/investors",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors"
              ],
              "query": [
                {
                  "key": "partner_customer_ref",
                  "value": "",
                  "disabled": false
                }
              ]
            },
            "description": "Resolves the partner_customer_ref you provisioned the investor with back to the investor. Use it when you hold your own key but not ours — previously the only way to recover the mapping was to re-POST /v1/investors and read the replay, which is a write used as a lookup. 404 if no investor of yours carries that ref.\n\n**Scopes:** `investors:read`\n\n**Errors:** 400, 401, 403, 404, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        },
        {
          "name": "Get a managed investor",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}"
              ]
            },
            "description": "Returns the investor's status, profile completeness and full profile. 404 if the investor does not belong to the calling partner (no existence oracle).\n\n**Scopes:** `investors:read`\n\n**Errors:** 401, 403, 404, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        },
        {
          "name": "Complete a managed investor's profile",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}",
                "profile"
              ]
            },
            "description": "PUT semantics — the profile fully replaces the stored profile state; domain value objects validate (IBAN checksum, BIC, birth date) and map failures to 422. The `address`, `bank_account` and `company` sections are each all-or-nothing: omit one entirely to leave it unset, or send it whole. Inside `bank_account` only `iban` is required — BIC and institution are derived from it and the account holder defaults to the profile name. `consents` is REQUIRED on every write: omitting it, or sending a consent as not-given, revokes that consent and erases its recorded capture instant, so re-send each consent as given to leave it untouched. 404 if the investor is not owned by the partner.\n\n**Scopes:** `investors:write`\n\n**Errors:** 401, 403, 404, 422, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"profile\": {\n    \"first_name\": \"Maria\",\n    \"last_name\": \"Santos\",\n    \"birth_date\": \"1985-03-15\",\n    \"tax_id\": \"DE12345678901\",\n    \"email\": \"maria@example.org\",\n    \"phone\": \"+49 30 1234567\",\n    \"address\": {\n      \"street\": \"Hauptstrasse\",\n      \"house_number\": \"1\",\n      \"postal_code\": \"10115\",\n      \"city\": \"Berlin\"\n    },\n    \"bank_account\": {\n      \"iban\": \"DE89370400440532013000\",\n      \"bic\": \"COBADEFFXXX\",\n      \"institution\": \"Commerzbank\",\n      \"account_holder\": \"Maria Santos\"\n    },\n    \"company\": {\n      \"name\": \"Solar Nord GmbH\",\n      \"legal_form\": \"GmbH\",\n      \"tax_id\": \"DE99999999999\"\n    },\n    \"consents\": {\n      \"terms\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      },\n      \"privacy\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      },\n      \"data_sharing\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      },\n      \"representation_authorization\": {\n        \"given\": true,\n        \"given_at\": \"2026-05-20T10:00:00Z\"\n      }\n    }\n  }\n}\n",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          }
        }
      ]
    },
    {
      "name": "Investments",
      "description": "B2B surface for partners to place and read share purchases and portfolio holdings for the investors they act on behalf of.",
      "item": [
        {
          "name": "List a managed investor's holdings",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}/holdings",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}",
                "holdings"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "size",
                  "value": "20",
                  "disabled": true
                }
              ]
            },
            "description": "Returns a page of the investor's portfolio positions — one row per cooperative with the current share count, oldest position first — as a paged envelope: `items` plus `page`, `size`, `total_items`, `total_pages`, `has_next`, `has_previous`. `page` is 1-based (default 1) and `size` defaults to 20 and is capped at 100 — a larger `size` is silently clamped to the cap rather than rejected. 404 if the investor is not owned by the partner.\n\n**Scopes:** `portfolio:read`\n\n**Errors:** 401, 403, 404, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        },
        {
          "name": "Place a share purchase for a managed investor",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "disabled": true
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}/share-purchases",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}",
                "share-purchases"
              ]
            },
            "description": "Drafts and submits a subscription order in one step, returning 201 with the SUBMITTED order. Commercial terms are computed from the cooperative's current pricing and frozen server-side — the request carries no price fields. Requires a complete investor profile (409 INVESTOR_PROFILE_INCOMPLETE otherwise). 404 if the investor is not owned by the calling partner. Only `cooperative_id`, `share_count`, `sepa_mandate.signed_at` and `statute_consent_given_at` are required: `applicant_type` follows the investor's company affiliation, `sepa_mandate.debtor_iban` defaults to the profile IBAN, and `sepa_mandate.reference` is issued server-side when omitted — each may still be sent explicitly to override. Terms and privacy consent are taken from the investor's profile and are no longer accepted here. Supply an optional Idempotency-Key header to make a retried create safe: a repeat with the same body replays the original order, a repeat with a changed body is rejected (409). Note that because `applicant_type` may be derived, editing the investor's company affiliation between two retries makes them different orders and so a 409. A key is honoured for 24 hours; after that it is forgotten, and reusing it places a NEW order rather than replaying the original.\n\n**Scopes:** `applications:write`\n\n**Errors:** 400, 401, 403, 404, 409, 422, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"cooperative_id\": \"{{cooperativeId}}\",\n  \"share_count\": 2,\n  \"applicant_type\": \"private\",\n  \"sepa_mandate\": {\n    \"debtor_iban\": \"DE89370400440532013000\",\n    \"reference\": \"UMR-1\",\n    \"signed_at\": \"2026-05-20T10:00:00Z\"\n  },\n  \"statute_consent_given_at\": \"2026-05-20T10:00:00Z\"\n}\n",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const body = pm.response.json();",
                  "if (body.share_purchase_id) {",
                  "    pm.collectionVariables.set(\"sharePurchaseId\", body.share_purchase_id);",
                  "}"
                ]
              }
            }
          ]
        },
        {
          "name": "List a managed investor's share purchases",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}/share-purchases",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}",
                "share-purchases"
              ],
              "query": [
                {
                  "key": "page",
                  "value": "1",
                  "disabled": true
                },
                {
                  "key": "size",
                  "value": "20",
                  "disabled": true
                }
              ]
            },
            "description": "Returns a page of the share-purchase orders the partner has placed for this investor, newest first, as a paged envelope: `items` plus `page`, `size`, `total_items`, `total_pages`, `has_next`, `has_previous`. `page` is 1-based (default 1) and `size` defaults to 20 and is capped at 100 — a larger `size` is silently clamped to the cap rather than rejected. 404 if the investor does not belong to the calling partner (no existence oracle).\n\n**Scopes:** `applications:read`\n\n**Errors:** 401, 403, 404, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        },
        {
          "name": "Get one of a managed investor's share purchases",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}/share-purchases/{{sharePurchaseId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}",
                "share-purchases",
                "{{sharePurchaseId}}"
              ]
            },
            "description": "404 for an unknown purchase id, one that belongs to a different investor, or an investor the calling partner does not own.\n\n**Scopes:** `applications:read`\n\n**Errors:** 401, 403, 404, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        },
        {
          "name": "Cancel a managed investor's share purchase",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/investors/{{investorId}}/share-purchases/{{sharePurchaseId}}/cancel",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "investors",
                "{{investorId}}",
                "share-purchases",
                "{{sharePurchaseId}}",
                "cancel"
              ]
            },
            "description": "Cancels a draft or submitted order; an already-settled or decided order yields 409. 404 for an unknown purchase, one belonging to a different investor, or an investor the calling partner does not own.\n\n**Scopes:** `applications:write`\n\n**Errors:** 401, 403, 404, 409, 429 — all RFC 9457 problem details with a stable `code` and a `trace_id`. See https://docs.valueverde.de/docs/error-reference."
          }
        }
      ]
    }
  ]
}
