{
  "info": {
    "name": "12 — Erros esperados",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "description": "Camada final: provoca os erros documentados (docs/b2b/ERROR_CATALOG.md) e confere status, error.code e requestId. Rode depois das coleções 01–11, com o mesmo ambiente. Nenhum caso envia ordem, paga ou conclui conexão. O último erro fica em lastErrorCode/lastErrorRequestId. Não incluídos: 429 RATE_LIMITED (exige estourar o limite) e 409 TENANT_MISMATCH (exige vários contratos)."
  },
  "auth": {
    "type": "basic",
    "basic": [
      {
        "key": "username",
        "value": "{{clientId}}",
        "type": "string"
      },
      {
        "key": "password",
        "value": "{{clientSecret}}",
        "type": "string"
      }
    ]
  },
  "item": [
    {
      "name": "Autenticação",
      "item": [
        {
          "name": "401 B2B_CREDENTIAL_INVALID — sem credencial",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/health",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "health"
              ],
              "query": []
            },
            "description": "Rota: GET /v1/health\n\nEsperado: HTTP 401 · B2B_CREDENTIAL_INVALID\n\nChamada sem Authorization.",
            "auth": {
              "type": "noauth"
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [401];",
                  "const codes = [\"B2B_CREDENTIAL_INVALID\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"sem-credencial\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "401 B2B_CREDENTIAL_INVALID — clientSecret errado",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/health",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "health"
              ],
              "query": []
            },
            "description": "Rota: GET /v1/health\n\nEsperado: HTTP 401 · B2B_CREDENTIAL_INVALID\n\nclientId certo, segredo inválido.",
            "auth": {
              "type": "basic",
              "basic": [
                {
                  "key": "username",
                  "value": "{{clientId}}",
                  "type": "string"
                },
                {
                  "key": "password",
                  "value": "segredo-errado-qa",
                  "type": "string"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [401];",
                  "const codes = [\"B2B_CREDENTIAL_INVALID\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"segredo-errado\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Idempotência",
      "item": [
        {
          "name": "400 IDEMPOTENCY_KEY_REQUIRED — mutação sem Idempotency-Key",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users"
              ],
              "query": []
            },
            "description": "Rota: POST /v1/users\n\nEsperado: HTTP 400 · IDEMPOTENCY_KEY_REQUIRED\n\nEscrita sem o cabeçalho obrigatório.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalUserId\": \"{{externalUserId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [400];",
                  "const codes = [\"IDEMPOTENCY_KEY_REQUIRED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"sem-chave\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "200/201 preparo — preparar conflito (1/2): upsert com chave fixa",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "erros-qa-conflito-{{externalUserId}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users"
              ],
              "query": []
            },
            "description": "Rota: POST /v1/users\n\nEsperado: HTTP 200/201\n\nPasso de preparo: cria/devolve o cliente sintético. Rode antes do 2/2.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalUserId\": \"{{externalUserId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [200,201];",
                  "const codes = [];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"conflito-1\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "409 IDEMPOTENCY_CONFLICT — mesma chave com outro corpo (2/2)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "erros-qa-conflito-{{externalUserId}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users"
              ],
              "query": []
            },
            "description": "Rota: POST /v1/users\n\nEsperado: HTTP 409 · IDEMPOTENCY_CONFLICT\n\nReusa a chave do 1/2 com corpo diferente.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"externalUserId\": \"{{externalUserId}}-outro\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [409];",
                  "const codes = [\"IDEMPOTENCY_CONFLICT\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"conflito-2\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Escopo e isolamento",
      "item": [
        {
          "name": "404 NOT_FOUND — cliente inexistente ou de outro parceiro",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/qa-erros-inexistente-0001",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "qa-erros-inexistente-0001"
              ],
              "query": []
            },
            "description": "Rota: GET /v1/users/qa-erros-inexistente-0001\n\nEsperado: HTTP 404 · NOT_FOUND\n\n404 não diferencia inexistente de alheio."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [404];",
                  "const codes = [\"NOT_FOUND\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"cliente-inexistente\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "404 NOT_FOUND — contrato (tenant) de outro parceiro",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/tenants/TENANT_ALHEIO_QA",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "tenants",
                "TENANT_ALHEIO_QA"
              ],
              "query": []
            },
            "description": "Rota: GET /v1/tenants/TENANT_ALHEIO_QA\n\nEsperado: HTTP 404 · NOT_FOUND\n\nId de contrato que não é seu."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [404];",
                  "const codes = [\"NOT_FOUND\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"contrato-alheio\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "403 SCOPE_FIELD_REJECTED — campo controlado pela plataforma no corpo",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/strategies",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "strategies"
              ],
              "query": []
            },
            "description": "Rota: PUT /v1/users/{externalUserId}/strategies\n\nEsperado: HTTP 403 · SCOPE_FIELD_REJECTED\n\nperformanceFeePercent é definido pelo FINAUTON.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"strategyIds\": [\n    \"{{strategyId}}\"\n  ],\n  \"performanceFeePercent\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403];",
                  "const codes = [\"SCOPE_FIELD_REJECTED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"campo-financeiro\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Grants e risco",
      "item": [
        {
          "name": "403 STRATEGY_NOT_AVAILABLE — estratégia fora do contrato",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/strategies",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "strategies"
              ],
              "query": []
            },
            "description": "Rota: PUT /v1/users/{externalUserId}/strategies\n\nEsperado: HTTP 403 · STRATEGY_NOT_AVAILABLE ou STRATEGY_NOT_GRANTED\n\nEstratégia não publicada/concedida.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"strategyIds\": [\n    \"NAO_CONCEDIDA_QA\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403];",
                  "const codes = [\"STRATEGY_NOT_AVAILABLE\",\"STRATEGY_NOT_GRANTED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"estrategia-nao-concedida\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "403 PROVIDER_NOT_ALLOWED — provider fora do contrato",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/setup?providerId=ASTER",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "setup"
              ],
              "query": [
                {
                  "key": "providerId",
                  "value": "ASTER"
                }
              ]
            },
            "description": "Rota: GET /v1/users/{externalUserId}/setup\n\nEsperado: HTTP 403 · PROVIDER_NOT_ALLOWED ou PROVIDER_NOT_GRANTED\n\nUse um provider que o seu contrato não inclui."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403];",
                  "const codes = [\"PROVIDER_NOT_ALLOWED\",\"PROVIDER_NOT_GRANTED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"provider-nao-concedido\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "403/422 RISK_ENVELOPE_EXCEEDED — alavancagem acima do teto do parceiro",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/setup/preview",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "setup",
                "preview"
              ],
              "query": []
            },
            "description": "Rota: POST /v1/users/{externalUserId}/setup/preview\n\nEsperado: HTTP 403/422 · RISK_ENVELOPE_EXCEEDED\n\nPrévia não grava nada.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"providerId\": \"{{providerId}}\",\n  \"mode\": \"REAL\",\n  \"enabled\": false,\n  \"leverage\": 125,\n  \"orderSizeUsd\": 10,\n  \"maxConcurrentTrades\": 1,\n  \"maxMarginPerTradeUsd\": 10,\n  \"maxDailyLossUsd\": 20\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403,422];",
                  "const codes = [\"RISK_ENVELOPE_EXCEEDED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"envelope\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "403 PAPER_NOT_GRANTED — modo PAPER no B2B",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/setup/preview",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "setup",
                "preview"
              ],
              "query": []
            },
            "description": "Rota: POST /v1/users/{externalUserId}/setup/preview\n\nEsperado: HTTP 403 · PAPER_NOT_GRANTED\n\nB2B opera só REAL.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"providerId\": \"{{providerId}}\",\n  \"mode\": \"PAPER\",\n  \"enabled\": false,\n  \"leverage\": 1,\n  \"orderSizeUsd\": 10,\n  \"maxConcurrentTrades\": 1,\n  \"maxMarginPerTradeUsd\": 10,\n  \"maxDailyLossUsd\": 20\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403];",
                  "const codes = [\"PAPER_NOT_GRANTED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"paper\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "400 VALIDATION_ERROR — maxConcurrentTrades acima de 3",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "erros-qa-simultaneas-{{externalUserId}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/risk",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "risk"
              ],
              "query": []
            },
            "description": "Rota: PUT /v1/users/{externalUserId}/risk\n\nEsperado: HTTP 400 · VALIDATION_ERROR\n\nAceito: 1 a 3.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"providerId\": \"{{providerId}}\",\n  \"maxConcurrentTrades\": 4,\n  \"maxMarginPerTradeUsd\": 10,\n  \"maxDailyLossUsd\": 10\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [400];",
                  "const codes = [\"VALIDATION_ERROR\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"simultaneas\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Conexão",
      "item": [
        {
          "name": "403 RETURN_URL_NOT_ALLOWED — returnUrl fora das origens registradas",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/connect-sessions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "connect-sessions"
              ],
              "query": []
            },
            "description": "Rota: POST /v1/users/{externalUserId}/connect-sessions\n\nEsperado: HTTP 403 · RETURN_URL_NOT_ALLOWED\n\nOrigem não cadastrada no seu contrato. Nenhuma sessão é criada.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"providerId\": \"{{providerId}}\",\n  \"returnUrl\": \"https://evil.example/x\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403];",
                  "const codes = [\"RETURN_URL_NOT_ALLOWED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"return-url\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "403 HOSTED_CONNECT_REQUIRED — chave de corretora enviada pela REST",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "erros-qa-hosted-{{externalUserId}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/accounts/{{providerId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "accounts",
                "{{providerId}}"
              ],
              "query": []
            },
            "description": "Rota: PUT /v1/users/{externalUserId}/accounts/{provider}\n\nEsperado: HTTP 403 · HOSTED_CONNECT_REQUIRED\n\nCliente de contrato só conecta pela página hospedada. Valores sintéticos.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"credentials\": {\n    \"apiKey\": \"qa-chave-falsa-0000\",\n    \"apiSecret\": \"qa-segredo-falso-0000\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [403];",
                  "const codes = [\"HOSTED_CONNECT_REQUIRED\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"segredo-pela-rest\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "422 CREDENTIALS_MISSING — leitura de conta sem corretora conectada",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/users/{{externalUserId}}/account?providerId={{providerId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "users",
                "{{externalUserId}}",
                "account"
              ],
              "query": [
                {
                  "key": "providerId",
                  "value": "{{providerId}}"
                }
              ]
            },
            "description": "Rota: GET /v1/users/{externalUserId}/account\n\nEsperado: HTTP 422 · CREDENTIALS_MISSING\n\nUse um externalUserId SEM conexão nesse provider (com conexão, a rota responde 200)."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const status = [422];",
                  "const codes = [\"CREDENTIALS_MISSING\"];",
                  "pm.test('HTTP ' + status.join('/'), () => pm.expect(status).to.include(pm.response.code));",
                  "let body = null; try { body = pm.response.json(); } catch (_) {}",
                  "if (codes.length) {",
                  "  const err = body && body.error;",
                  "  pm.test('Envelope { error }', () => pm.expect(err).to.be.an('object'));",
                  "  pm.test('error.code = ' + codes.join(' | '), () => pm.expect(codes).to.include(err && err.code));",
                  "  pm.test('requestId presente', () => pm.expect(err && err.requestId).to.be.a('string'));",
                  "  pm.environment.set('lastErrorCode', (err && err.code) || '');",
                  "  pm.environment.set('lastErrorRequestId', (err && err.requestId) || '');",
                  "  console.log('[erro]', \"sem-conexao\", pm.response.code, err && err.code, err && err.requestId, err && err.message);",
                  "}"
                ]
              }
            }
          ],
          "response": []
        }
      ]
    }
  ]
}
