{
  "info": {
    "name": "Atlas API",
    "description": "Resolve your supplier records to verified legal entities, then enrich them with registration, firmographic, identifier, and corporate hierarchy data.\n\nSet `clientId` and `clientSecret` in the collection variables, run Request an access token, then the other requests. The token and the latest jobId are saved for you.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{accessToken}}",
        "type": "string"
      }
    ]
  },
  "item": [
    {
      "name": "Authentication",
      "item": [
        {
          "name": "Request an access token",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/auth/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "auth",
                "login"
              ],
              "query": []
            },
            "description": "Call this first. Log in once, then reuse the token for every other request until shortly before it expires. Limit: 10 logins per minute per organization.\n\nExchange your `clientId` and `clientSecret` for a bearer token (JWT). The response follows the OAuth 2.0 token shape (RFC 6749).\n\n- The response does **not** include `expires_in`. Read the `exp` claim of the JWT.\n- There is no refresh token and no logout endpoint. Call this endpoint again when the token expires or a request returns `401`.\n- Cache the token and reuse it until shortly before `exp`.\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Content-Type` | Yes | The request body is JSON. | `application/json` |\n\n**Request body**\n\n| Field | Type | Required | Description | Example |\n| --- | --- | --- | --- | --- |\n| `clientId` | string | Yes | Your client ID. | `<your-client-id>` |\n| `clientSecret` | string | Yes | Your client secret. Keep it server-side. | `<your-client-secret>` |\n",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"clientId\": \"{{clientId}}\",\n  \"clientSecret\": \"{{clientSecret}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Logged in\", () => pm.response.to.have.status(200));",
                  "pm.collectionVariables.set(\"accessToken\", pm.response.json().access_token);"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Match",
      "item": [
        {
          "name": "Submit a batch for matching",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/match/jobs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "match",
                "jobs"
              ],
              "query": []
            },
            "description": "Use this to start matching a batch of supplier records to legal entities. It returns a `jobId` straight away and the matching runs in the background. A `202` means the job was accepted, not finished. Next, call `getMatchJob` to check progress and `getMatchJobResults` to read results.\n\nLimits: 20 submit requests per minute per organization, and up to 20,000 records per request. A larger batch returns `413`. After a `429`, wait the number of seconds in the `Retry-After` header. If a request times out, check before you retry, because a retry can create a second job.\n\nSubmit a batch of your supplier records. Each record needs `internalSupplierId` and `companyName`. More identifiers produce better matches.\n\nDuplicate `internalSupplierId` values in a batch are accepted and processed independently.\n\nNot supported on submit: `maxCandidates` (a parameter of the results request), `matchPriority`, and spend or product fields (`productDescription`, `spendDate`, `spendAmount`, `spendCurrency`).\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Content-Type` | Yes | The request body is JSON. | `application/json` |\n| `Authorization` | Yes | The bearer token from **Request an access token**. | `Bearer <access_token>` |\n\n**Request body**\n\n| Field | Type | Required | Description | Example |\n| --- | --- | --- | --- | --- |\n| `records` | array of object | Yes | The supplier records to match. At least one is required. |  |\n| `records[].internalSupplierId` | string | Yes | Unique ID of the supplier record in your system. Links results back to your records. | `12345` |\n| `records[].companyName` | string | Yes | Supplier name. Use the legal business name where you can. Often the primary matching signal. | `Dunder Mifflin Inc.` |\n| `records[].atlasId` | string | No | A known Atlas ID. When provided, the record is forced to match this specific entity and search is bypassed. | `eb666ad4-d1e5-4a57-9e46-ff26acf5d92e` |\n| `records[].internalSourceName` | string | No | Your system-of-record label, for example `SAP`, `Workday` or `OracleERP`. Not related to how Supplier.io sources data. | `SAP` |\n| `records[].alternateNames` | array of string | No | Aliases, former names or DBAs. | `[\"Dunder Mifflin Paper Co\",\"DMI Inc\"]` |\n| `records[].completeAddress` | string | No | Full address. Separate multiple addresses with a pipe (`\\|`). When present, the component address fields are ignored for matching. | `1725 Slough Avenue, Scranton, PA 18505, USA` |\n| `records[].streetAddress` | string | No | Street name, building number, suite. | `1725 Slough Avenue` |\n| `records[].city` | string | No | City. | `Scranton` |\n| `records[].state` | string | No | State or province, full name or abbreviation. | `PA` |\n| `records[].country` | string | No | ISO 3166-1 country code or common name preferred. | `US` |\n| `records[].zipPostalCode` | string | No | ZIP or postal code. | `18505` |\n| `records[].taxId` | string | No | Tax identification number (EIN, VAT and so on). | `23-1234567` |\n| `records[].taxIdLocality` | string | No | Jurisdiction or country of the tax ID (ISO 3166-1 preferred). | `US` |\n| `records[].lei` | string (20 characters) | No | Legal Entity Identifier issued by GLEIF. | `549300ABCDEF12345678` |\n| `records[].legalEntityJurisdictionCode` | string | No | Jurisdiction code of the company's registration. ISO 3166-1 alpha-2 for country, ISO 3166-2 for subdivision. Combined with the company number, it forms a unique identifier. | `us_de` |\n| `records[].legalEntityCompanyNumber` | string | No | Registration number in that jurisdiction. | `4762854` |\n| `records[].dunsNumber` | string (9 digits) | No | D-U-N-S number. Input only. Not returned in Match responses. | `123456789` |\n| `records[].spgCompanyNumber` | string or number | No | S&P company number. Input only. | `23815047` |\n| `records[].samUei` | string (12 characters) | No | U.S. SAM Unique Entity Identifier. | `JH8MK12NABC5` |\n| `records[].cageCode` | string (5 characters) | No | Commercial and Government Entity code. | `1A2B3` |\n| `records[].businessId` | string | No | Another business registration ID not covered by tax ID or company number, for example a Canadian BN or Australian ABN. | `123456789RC0001` |\n| `records[].businessIdLocality` | string | No | Jurisdiction or country of the business ID. | `CA` |\n| `records[].webDomain` | array of string | No | One or more web domains. | `[\"dundermifflin.com\"]` |\n| `records[].contactEmail` | string (email) | No | An email address associated with the supplier. | `info@dundermifflin.com` |\n| `records[].stockTicker` | string | No | Stock ticker, if publicly traded. | `DMI` |\n| `records[].secCik` | string (1 to 10 digits) | No | SEC Central Index Key for U.S. public companies. The standard form is 10 digits, zero-padded. | `0000012345` |\n| `records[].isinCode` | string (12 characters) | No | International Securities Identification Number (ISIN). | `US0378331005` |\n",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"records\": [\n    {\n      \"internalSupplierId\": \"12345\",\n      \"companyName\": \"Dunder Mifflin Inc.\",\n      \"internalSourceName\": \"SAP\",\n      \"alternateNames\": [\n        \"Dunder Mifflin Paper Co\",\n        \"DMI Inc\"\n      ],\n      \"completeAddress\": \"1725 Slough Avenue, Scranton, PA 18505, USA\",\n      \"taxId\": \"23-1234567\",\n      \"taxIdLocality\": \"US\",\n      \"webDomain\": [\n        \"dundermifflin.com\"\n      ],\n      \"contactEmail\": \"info@dundermifflin.com\"\n    },\n    {\n      \"internalSupplierId\": \"12346\",\n      \"companyName\": \"Acme Consulting LLC\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Batch accepted\", () => pm.response.to.have.status(202));",
                  "pm.collectionVariables.set(\"jobId\", pm.response.json().jobId);"
                ]
              }
            }
          ]
        },
        {
          "name": "Get match job status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/match/jobs/{{jobId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "match",
                "jobs",
                "{{jobId}}"
              ],
              "query": []
            },
            "description": "Use this to check the progress of a match job. Poll every few seconds at first, then less often for large jobs. You do not have to wait for `COMPLETED` to read results. Limit: 120 status and results requests per minute per organization.\n\nReturns the job status and counters. `processedCount` equals `matchedCount + noMatchCount + failedCount`. When the status is `COMPLETED`, those three counters add up to `totalRecords`.\n\n`totalRecords` can be smaller than you sent. If your connection drops mid-upload, the server processes the complete records it received. Treat missing records as *not submitted*, not as *no match*.\n\nA `FAILED` job returns only `jobId`, `status` and `error`.\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Authorization` | Yes | The bearer token from **Request an access token**. | `Bearer <access_token>` |\n"
          }
        },
        {
          "name": "Get match results",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/match/jobs/{{jobId}}/results?maxCandidates=5",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "match",
                "jobs",
                "{{jobId}}",
                "results"
              ],
              "query": [
                {
                  "key": "maxCandidates",
                  "value": "5",
                  "description": "Maximum candidates returned per record. A value outside 0 to 50, or a non-integer, returns `400`. `0` returns an empty `candidates` array for every record.",
                  "disabled": true
                }
              ]
            },
            "description": "Use this to read match results. Each record has its own outcome in `matchStatus`: `MATCHED`, `AMBIGUOUS`, `NO_MATCH`, `FAILED`, `INVALID`, or `PROCESSING`. `NO_MATCH` is a successful result, not an error. A `FAILED` record is a per-record failure, not an HTTP error, and `failureReason` says whether to retry it as is (`PROCESSING_ERROR`) or correct the `atlasId` (`INVALID_ATLAS_ID`). Match results to your own records with `internalSupplierId`. Duplicates in a batch are not merged, so use the position in the `results` array for duplicates. Limit: 120 status and results requests per minute per organization.\n\nReturns results for the records finished so far. You can call this at any time, even while the job is running. Later calls return more results.\n\n`candidates` is always an array and is sorted by `matchScore` from high to low. Inactive and dissolved entities are returned as well.\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Authorization` | Yes | The bearer token from **Request an access token**. | `Bearer <access_token>` |\n\n**Query parameters**\n\n| Parameter | Type | Required | Default | Description | Example |\n| --- | --- | --- | --- | --- | --- |\n| `maxCandidates` | integer (0 to 50) | No | `5` | Maximum candidates returned per record. A value outside 0 to 50, or a non-integer, returns `400`. `0` returns an empty `candidates` array for every record. | `3` |\n"
          }
        }
      ]
    },
    {
      "name": "Enrichment",
      "item": [
        {
          "name": "Submit a batch for enrichment",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/enrichment/jobs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "enrichment",
                "jobs"
              ],
              "query": []
            },
            "description": "Use this to get the full profile of legal entities you have already identified. Each record needs an Atlas ID, which you get from `getMatchJobResults`. It returns a `jobId` straight away and the work runs in the background. A `202` means the job was accepted, not finished. Next, call `getEnrichmentJob` to check progress and `getEnrichmentJobResults` to read results.\n\nLimits: 20 submit requests per minute per organization, and up to 20,000 records per request. A larger batch returns `413`. After a `429`, wait the number of seconds in the `Retry-After` header. If a request times out, check before you retry, because a retry can create a second job.\n\nSubmit a batch of Atlas IDs. Enrichment does not match records. Obtain Atlas IDs from Match or another prior source.\n\nDuplicate `atlasId` values in a batch are accepted. Each is processed independently.\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Content-Type` | Yes | The request body is JSON. | `application/json` |\n| `Authorization` | Yes | The bearer token from **Request an access token**. | `Bearer <access_token>` |\n\n**Request body**\n\n| Field | Type | Required | Description | Example |\n| --- | --- | --- | --- | --- |\n| `records` | array of object | Yes | The Atlas IDs to enrich. At least one is required. |  |\n| `records[].atlasId` | string | Yes | The Atlas ID to enrich. | `eb666ad4-d1e5-4a57-9e46-ff26acf5d92e` |\n| `records[].internalSupplierId` | string | Yes | Your ID for this record. Echoed back in results. | `12345` |\n| `records[].internalSourceName` | string | No | Your system-of-record label. Echoed back in results. | `SAP` |\n",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"records\": [\n    {\n      \"atlasId\": \"eb666ad4-d1e5-4a57-9e46-ff26acf5d92e\",\n      \"internalSupplierId\": \"12345\",\n      \"internalSourceName\": \"SAP\"\n    },\n    {\n      \"atlasId\": \"3a02eaea-22ab-48be-8ce7-3f4a199c374a\",\n      \"internalSupplierId\": \"12346\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test(\"Batch accepted\", () => pm.response.to.have.status(202));",
                  "pm.collectionVariables.set(\"jobId\", pm.response.json().jobId);"
                ]
              }
            }
          ]
        },
        {
          "name": "Get enrichment job status",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/enrichment/jobs/{{jobId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "enrichment",
                "jobs",
                "{{jobId}}"
              ],
              "query": []
            },
            "description": "Use this to check the progress of an enrichment job. Poll every few seconds at first, then less often for large jobs. You do not have to wait for `COMPLETED` to read results. Limit: 120 status and results requests per minute per organization.\n\nReturns the job status and counters. When the status is `COMPLETED`, `matchedCount + noMatchCount + failedCount` equals `processedCount`, which equals `totalRecords`.\n\nA `FAILED` job returns only `jobId`, `status` and `error`.\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Authorization` | Yes | The bearer token from **Request an access token**. | `Bearer <access_token>` |\n"
          }
        },
        {
          "name": "Get enrichment results",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/enrichment/jobs/{{jobId}}/results",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "enrichment",
                "jobs",
                "{{jobId}}",
                "results"
              ],
              "query": []
            },
            "description": "Use this to read enrichment results. Each record has its own `status`: `SUCCESS` or `FAILED`. A `FAILED` record is a per-record failure, not an HTTP error. `failureReason` says whether to retry it as is (`PROCESSING_ERROR`) or correct the `atlasId` (`INVALID_ATLAS_ID`). Match results to your own records with `internalSupplierId`. Limit: 120 status and results requests per minute per organization.\n\nReturns results for the records finished so far. There are no query parameters. Like Match, you can call this while the job runs.\n\n`data` is `null` when a record's `status` is `FAILED`. Groups that depend on your entitlements, such as `hierarchy`, are `null` when they are not available to you.\n\n**Headers**\n\n| Header | Required | Description | Example |\n| --- | --- | --- | --- |\n| `Authorization` | Yes | The bearer token from **Request an access token**. | `Bearer <access_token>` |\n"
          }
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://api.supplier.io/atlas"
    },
    {
      "key": "clientId",
      "value": "",
      "type": "string"
    },
    {
      "key": "clientSecret",
      "value": "",
      "type": "secret"
    },
    {
      "key": "accessToken",
      "value": "",
      "type": "secret"
    },
    {
      "key": "jobId",
      "value": ""
    }
  ]
}
