{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Direct Integrator Authentication","meta":[{"name":"robots","content":"noindex"}]},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"direct-integrator-authentication","__idx":0},"children":["Direct Integrator Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This flow lets a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["direct integrator"]}," — a partner integrating their own single account (as opposed to an aggregator managing many accounts) — obtain the credentials needed to call the API."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["It complements the existing auth documentation:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://developers.wallapop.com/pages/api-essentials/auth"},"children":["API Essentials — Authentication"]}," — general OAuth 2.0 concepts used across the APIs."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://developers.wallapop.com/pages/guides/quickstart"},"children":["Guides — Quickstart"]}," — end-to-end walkthrough for aggregators."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you are integrating multiple end-user accounts on behalf of your customers, use the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["aggregator (Authorization Code + PKCE)"]}," flow described in the guides above. If you are integrating your own single commercial account, use the flow described below."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"who-this-is-for","__idx":1},"children":["Who this is for"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You have a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["commercial subscription"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You are integrating ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["your own account"]}," one seller, one integration."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You want a low-friction way to obtain credentials without implementing the full authorization flow."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":2},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The flow has three steps:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Get a refresh token"]}," — you log into a hosted web portal with your account credentials. The portal displays a long-lived refresh token you copy into your backend."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Exchange the refresh token for an access token"]}," — your backend calls the token endpoint whenever it needs a new access token. Each call also returns a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["new"]}," refresh token that replaces the one you used."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use the access token"]}," — send it as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Bearer"]}," header to any API endpoint, exactly like in the other auth flows."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"┌────────────┐      1. Log in       ┌──────────────────────────┐\n│  Operator  │ ───────────────────▶ │  Hosted portal           │\n│ (browser)  │ ◀─── refresh token ──│  /realms/…/auth          │\n└────────────┘                      └──────────────────────────┘\n      │\n      │ copy refresh token\n      ▼\n┌────────────┐                      ┌──────────────────────────┐\n│  Partner   │ 2. POST /token       │   Token endpoint         │\n│  backend   │ ───────────────────▶ │                          │\n│            │ ◀── access + NEW ────│                          │\n│            │     refresh token    └──────────────────────────┘\n│            │\n│            │ 3. Authorization: Bearer <access>\n│            │ ───────────────────▶ Any API endpoint\n└────────────┘\n"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1--get-a-refresh-token","__idx":3},"children":["Step 1 — Get a refresh token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Open the portal in a browser:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://iam.wallapop.com/realms/wallapop-connect/auth\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Enter the email and password of the account you want to integrate with, and submit the form."," ","On success, the portal displays your ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["refresh token"]},". Copy it into a secure store on your backend."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Keep the refresh token safe and treat it like a password."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2--exchange-the-refresh-token-for-an-access-token","__idx":4},"children":["Step 2 — Exchange the refresh token for an access token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Send a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," to the token endpoint each time you need a fresh access token:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST 'https://iam.wallapop.com/realms/wallapop-connect/protocol/openid-connect/token' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  --data-urlencode 'grant_type=refresh_token' \\\n  --data-urlencode 'client_id=DirectIntegration' \\\n  --data-urlencode 'refresh_token=<YOUR_REFRESH_TOKEN>'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Successful response (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]},"):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"access_token\": \"eyJhbGciOi...\",\n  \"expires_in\": 300,\n  \"refresh_expires_in\": 1209600,\n  \"refresh_token\": \"eyJhbGciOi...\",\n  \"token_type\": \"Bearer\",\n  \"scope\": \"openid basic offline_access\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Important fields:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["access_token"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Bearer token to use on API calls. Valid for ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["5 minutes"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expires_in: 300"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["refresh_token"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A new refresh token"]},". The one you just used is now invalidated (see \"Rotation\" below). Persist this new value and use it on the next refresh."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["refresh_expires_in"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Lifetime of the new refresh token in seconds (",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["14 days"]},")."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"refresh-token-rotation--read-this-carefully","__idx":5},"children":["Refresh-token rotation — read this carefully"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Refresh tokens are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["single-use"]}," and rotate on every call:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You call ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/token"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R1"]}," → response contains new access token AND a new refresh token ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R2"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R1"]}," is now invalidated. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["You must persist ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R2"]}," and use it on the next call."]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Next call with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R2"]}," → response returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R3"]},". Persist ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["R3"]},", and so on."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["If you present the same refresh token twice"]}," (either because you didn't persist the rotation, or because someone else copied it), the token is treated as compromised:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The call returns ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["400 Bad Request"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"error\": \"invalid_grant\"}"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The entire session is revoked."]}," Every refresh token that has ever been issued from this login is invalidated — including the one currently in your backend."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Recovery: go back to Step 1 and log into the portal again to obtain a fresh refresh token."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For robust integration:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Persist the new refresh token ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["before"]}," treating the token call as successful."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Serialize refresh calls per account (avoid two backend workers calling ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/token"]}," with the same refresh token in parallel)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Handle ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_grant"]}," by triggering an operator alert — the account owner must re-run Step 1."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3--call-the-apis","__idx":6},"children":["Step 3 — Call the APIs"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the access token as a standard bearer token:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl 'https://connect.wallapop.com/<endpoint>' \\\n  -H 'Authorization: Bearer <YOUR_ACCESS_TOKEN>'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Because access tokens expire in 5 minutes, most integrations refresh proactively — for example, refreshing every 4 minutes, or lazily on the first ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}," response from an API call."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"which-flow-do-i-need","__idx":7},"children":["Which flow do I need?"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Two different integrations, two different flows:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Your situation"},"children":["Your situation"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Flow to use"},"children":["Flow to use"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["You are integrating ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["multiple end-user accounts"]}," on behalf of your customers (e.g. you build a listings-management SaaS)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Aggregator (",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/pages/api-essentials/auth"},"children":["Authorization Code + PKCE"]},")"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["You are integrating ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["your own single commercial account"]}," (e.g. your storefront)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/pages/api-essentials/direct-integrator-auth"},"children":["Direct Integrator Authentication"]}]}," — hosted login portal, minimal integration on your side"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you're not sure, you're almost certainly an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["aggregator"]}," — keep reading this page. The direct-integrator flow is reserved for partners with a commercial subscription who want to authenticate their own account without implementing the full authorization flow."]}]},"headings":[{"value":"Direct Integrator Authentication","id":"direct-integrator-authentication","depth":1},{"value":"Who this is for","id":"who-this-is-for","depth":2},{"value":"Overview","id":"overview","depth":2},{"value":"Step 1 — Get a refresh token","id":"step-1--get-a-refresh-token","depth":2},{"value":"Step 2 — Exchange the refresh token for an access token","id":"step-2--exchange-the-refresh-token-for-an-access-token","depth":2},{"value":"Refresh-token rotation — read this carefully","id":"refresh-token-rotation--read-this-carefully","depth":3},{"value":"Step 3 — Call the APIs","id":"step-3--call-the-apis","depth":2},{"value":"Which flow do I need?","id":"which-flow-do-i-need","depth":2}],"frontmatter":{"title":"Direct Integrator Authentication","description":"How direct integrators (single-account, self-serve) authenticate with the Connect API.","seo":{"title":"Direct Integrator Authentication"}},"lastModified":"2026-09-29T15:34:47.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/pages/api-essentials/direct-integrator-auth","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}