openapi: 3.1.0 info: version: 2.0.0 title: AML API OAuth Assets Customers API x-explorer-description: '# Welcome to the Elliptic API Explorer This area allows you to experiment with API requests in your browser. Browse to a request of interest and `Try it out`. If you are looking for the full documentation, head over to [the docs section](/developers/docs) ## Authentication Authentication in this explorer is accomplished using a JWT bearer token. This will allow your logged in user to make requests to our API if permitted. API authentication does not use JWTs, please see the [auth cookbooks](https://app.elliptic.co/#section/Cookbooks/Authentication) in the documentation for instructions on how to authenticate with our API programatically. ## Environment **Any requests made here will run against the environment you are logged in to. We advise using your sandbox environment so as not to pollute your production dataset** ' description: "# Introduction\n**Welcome to the Elliptic API Documentation**\n\nIn its simplest form, the Elliptic API allows you to [submit batches of transactions programmatically](#operation/analysisBatch), without human intervention. By connecting directly from your internal systems, you control what is submitted for analysis and when. Your compliance team can then log into the Elliptic web interface and analyse the results of the requested transaction screenings.\n\nBeyond batch-submitting transactions, the Elliptic API can also be used to obtain immediate responses to transaction or address analysis requests through our [synchronous endpoint](#operation/analysisSync). This option returns the result of the analysis in the same request in case you want to use the information for immediate reference. This is only recommended if you have a low volume of transactions (due to API rate limiting) and you need access to the results immediately for compliance workflow reasons. If you have high volumes or do not need to make time critical responses, use the [batch endpoint](#operation/analysisBatch).\n\nThe API can also be used to interrogate your analyzed customers, transactions, addresses and risk rules, pulling information at scale, with the filters of your choice. For example, if you want to import all the analyzed transactions with the respective dollar amount, risk score, CustomerID, etc into your Transaction Monitoring System you can do so by using the GET endpoints. You can find more information about this in the [get all analysis results section](#operation/getAllAnalyses).\n\nAdditionally, for more advanced use-cases, the Elliptic API can also be used to carry out workflows programmatically (i.e. set transactions notes etc.). This is particularly helpful if you are using Elliptic through your own bespoke built Transaction Monitoring System, rather than the Elliptic AML web interface.\n\n# Experimenting with the API\nIf you'd like to make some requests with the API in your browser, head over to the [API explorer](/developers/explorer)\n\n## Getting Started\n1. First, request your API keys through your Customer Success Manager or via [customers@elliptic.co](mailto:customers@elliptic.co). For privacy reasons, you will be asked for a keybase username or PGP Public Key;\n2. [Authenticate with the API](#section/Authentication), using the credentials provided in step 1 as this will allow you to pass our API’s user verification;\n3. [Submit your first test request](#operation/analysisSync). The submitted transactions will appear immediately in the Elliptic AML web interface.\n# Configuration\n### Types\n All timestamps are represented as ISO8601-formatted date strings with time zone.\n### Requests\n All HTTP requests and responses are application/json content type and typical HTTP response status codes for success and failure are used.\n All successful requests will respond with an HTTP 2xx status code and will contain a body (except in the case of a 204). The body varies by endpoint and will be described for each resource below, but will contain a JSON data object or array (for individual and multiple resources, respectively).\n All other requests will respond with an HTTP 4xx or 5xx status code. Furthermore, the body will contain an array of error messages to help with understanding the cause of failure.\n### Rate Limiting\n There is a global limit of 500 requests per minute. This applies to all requests (GET, POST, etc.).\n If the limit is reached an HTTP 429 status code will be returned.\n\n The following headers are returned by each request\n\n Header | Description\n ------ | -----------\n X-RateLimit-Limit | The limit of the current endpoint\n X-RateLimit-Remaining | The remaining rate limit\n X-RateLimit-Reset | The timestamp after which the limit will reset (unix epoch timestamp)\n\n# Authentication\nAll HTTP requests to API endpoints require authentication and authorization. For code samples see our [Authenication Cookbooks](#section/Cookbooks/Authentication)\n\nElliptic will provide an API key and secret which must be used to set HTTP headers. These headers will be used to verify and authorize all requests.\n\nThe following headers should be added to all HTTP requests:\n\nKey | Value\n--- | ---\nx-access-key | \\\nx-access-sign | \\\nx-access-timestamp | \\ (in milliseconds)\n\nExamples of how to generate the signature can be found to the below. The following variables are referenced:\n\nVariable | Description\n------- | ---------\nTIME_OF_REQUEST_IN_MS | The current time formatted as the current unix timestamp (in milliseconds)\nSIGNATURE_OF_REQUEST | A Base64 string encoded HMAC-SHA256 of REQUEST_TEXT signed with the Base64 decoded SECRET\nREQUEST_TEXT | a plain string concatenation of: TIME_OF_REQUEST_IN_MS, HTTP_METHOD (uppercase), HTTP_PATH (lowercase API path including query string), REQUEST_BODY (string encoded JSON object, or “{}” if there is no body)\n\nFrom now on, all code examples will assume you are attaching the authentication headers\n\n# Cookbooks\n\n## Authentication\nWe've provided Authentication cookbooks for commonly used langauges below. If your language isn't listed here, [let us know](mailto:customers@elliptic.co) and we'll add it\n\n\n\n\n
Java\n\n```java\nimport javax.crypto.Mac;\nimport javax.crypto.spec.SecretKeySpec;\nimport org.apache.commons.codec.binary.Base64;\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\npublic class EllipticAuth {\n /*\n * Generate a signature for use when signing a request to the API\n *\n * - secret: your secret supplied by Elliptic - a base64 encoded string\n * - time_of_request: current time, in milliseconds, since 1 Jan 1970 00:00:00 UTC\n * - http_method: must be uppercase\n * - http_path: API endpoint including query string\n * - payload: string encoded JSON object or \"{}\" if there is no request body\n */\n public static String get_signature(String secret, String time_of_request, String http_method, String http_path, String payload) {\n\n try {\n // create a SHA256 HMAC using the supplied secret, decoded from base64\n Mac hmac = Mac.getInstance(\"HmacSHA256\");\n SecretKeySpec secret_key = new SecretKeySpec(Base64.decodeBase64(secret), \"HmacSHA256\");\n hmac.init(secret_key);\n\n // concatenate the request text to be signed\n String request_text = time_of_request + http_method + http_path.toLowerCase() + payload;\n\n // update the HMAC with the text to be signed\n hmac.update(request_text.getBytes());\n\n // output the signature as a base64 encoded string\n return Base64.encodeBase64String(hmac.doFinal());\n } catch(InvalidKeyException | NoSuchAlgorithmException e) {\n throw new RuntimeException(e);\n }\n }\n\n public static String SECRET = \"894f142d667e8cdaca6822ac173937af\"; // Supplied by Elliptic - a base64 encoded string\n // Disclaimer: this secret is just an example\n public static String TIME_OF_REQUEST_IN_MS = \"1478692862000\"; // For real world use currentTimeMillis()\n public static String EXAMPLE_PAYLOAD = \"[{\\\"customer_reference\\\":\\\"123456\\\",\\\"subject\\\":{\\\"asset\\\":\\\"BTC\\\",\\\"hash\\\":\\\"accf5c09cc027339a3beb2e28104ce9f406ecbbd29775b4a1a17ba213f1e035e\\\",\\\"output_address\\\":\\\"15Hm2UEPaEuiAmgyNgd5mF3wugqLsYs3Wn\\\",\\\"output_type\\\":\\\"address\\\",\\\"type\\\":\\\"transaction\\\"},\\\"type\\\":\\\"source_of_funds\\\"}]\";\n\n public static void main(String[] args) {\n // Example One: POST with payload\n System.out.println(get_signature(EllipticAuth.SECRET, EllipticAuth.TIME_OF_REQUEST_IN_MS, \"POST\", \"/v2/analyses\", EXAMPLE_PAYLOAD));\n // 65mQHB2o95lL3I+N/bZYwDC9p2YvNwsVDnXr8u72hUk=\n\n // Example Two: GET with empty payload\n System.out.println(get_signature(EllipticAuth.SECRET, EllipticAuth.TIME_OF_REQUEST_IN_MS, \"GET\", \"/v2/customers\", \"{}\"));\n // cN9fRUqeT7UnwwpkBZaNmnwxKAPHkhytdXelfUVvxMI=\n return;\n }\n}\n```\n\n
\n\n
Ruby\n\n```ruby\nrequire 'base64'\nrequire 'json'\nrequire 'openssl'\n\n=begin\n Generate a signature for use when signing a request to the API\n\n - secret: your secret supplied by Elliptic - a base64 encoded string\n - time_of_request: current time, in milliseconds, since 1 Jan 1970 00:00:00 UTC\n - http_method: must be uppercase\n - http_path: API endpoint including query string\n - payload: string encoded JSON object or '{}' if there is no request body\n=end\ndef get_signature(secret, time_of_request, http_method, http_path, payload)\n # concatenate the request text to be signed\n request_text = time_of_request + http_method + http_path.downcase + payload\n\n # create a SHA256 HMAC using the supplied secret, decoded from base64, and update it with the request_text\n hmac = OpenSSL::HMAC.digest('SHA256', Base64.decode64(secret), request_text)\n\n # output the signature as a base64 encoded string\n signed = Base64.encode64(hmac).strip.encode('UTF-8')\nend\n\nSECRET = '894f142d667e8cdaca6822ac173937af' # Supplied by Elliptic\n# Disclaimer: this secret is just an example\nTIME_OF_REQUEST_IN_MS = '1478692862000' # For real world use (Time.now.to_i * 1000)\nEXAMPLE_PAYLOAD = [\n {\n \"customer_reference\": \"123456\",\n \"subject\": {\n \"asset\": \"BTC\",\n \"hash\": \"accf5c09cc027339a3beb2e28104ce9f406ecbbd29775b4a1a17ba213f1e035e\",\n \"output_address\": \"15Hm2UEPaEuiAmgyNgd5mF3wugqLsYs3Wn\",\n \"output_type\": \"address\",\n \"type\": \"transaction\"\n },\n \"type\": \"source_of_funds\"\n }\n]\n\n# Example One: POST with payload - you only need to run JSON.generate when passing a request body\nputs(get_signature(SECRET, TIME_OF_REQUEST_IN_MS, 'POST', '/v2/analyses', JSON.generate(EXAMPLE_PAYLOAD)))\n# 65mQHB2o95lL3I+N/bZYwDC9p2YvNwsVDnXr8u72hUk=\n\n# Example Two: GET with empty payload - do not run JSON.generate with no request body, pass an empty object as string\nputs(get_signature(SECRET, TIME_OF_REQUEST_IN_MS, 'GET', '/v2/customers', '{}'))\n# cN9fRUqeT7UnwwpkBZaNmnwxKAPHkhytdXelfUVvxMI\n```\n\n
\n\n
C#\n\n```c#\nusing System;\nusing System.Security.Cryptography;\nusing System.Text;\n\npublic class EllipticAuth\n{\n\n /// Generate a signature for use when signing a request to the API\n /// your secret supplied by Elliptic - a base64 encoded string\n /// current time, in milliseconds, since 1 Jan 1970 00:00:00 UTC\n /// must be uppercase\n /// API endpoint including query string\n /// string encoded JSON object or '{}' if there is no request body\n public static String get_signature(string secret, string time_of_request, string http_method, string http_path, string payload) {\n string output = \"\";\n\n // create a SHA256 HMAC using the supplied secret, decoded from base64\n var encoding = new ASCIIEncoding();\n byte[] keyByte = Convert.FromBase64String(secret);\n\n // concatenate the request text to be signed\n string request_text = time_of_request + http_method + http_path + payload;\n\n // update the HMAC with the text to be signed\n byte[] msgBytes = encoding.GetBytes(request_text);\n using (var hmac = new HMACSHA256(keyByte))\n {\n byte[] hashed = hmac.ComputeHash(msgBytes);\n // output the signature as a base64 encoded string\n output = Convert.ToBase64String(hashed);\n }\n\n return output;\n }\n\n public static string SECRET = \"894f142d667e8cdaca6822ac173937af\"; // Supplied by Elliptic - a base64 encoded string\n // Disclaimer: this secret is just an example\n public static string TIME_OF_REQUEST_IN_MS = \"1478692862000\"; // For real world use DateTimeOffset.Now.ToUnixTimeMilliseconds();\n public static string EXAMPLE_PAYLOAD = \"[{\\\"customer_reference\\\":\\\"123456\\\",\\\"subject\\\":{\\\"asset\\\":\\\"BTC\\\",\\\"hash\\\":\\\"accf5c09cc027339a3beb2e28104ce9f406ecbbd29775b4a1a17ba213f1e035e\\\",\\\"output_address\\\":\\\"15Hm2UEPaEuiAmgyNgd5mF3wugqLsYs3Wn\\\",\\\"output_type\\\":\\\"address\\\",\\\"type\\\":\\\"transaction\\\"},\\\"type\\\":\\\"source_of_funds\\\"}]\";\n\n public static void Main()\n {\n // Example One: POST with payload\n Console.WriteLine(get_signature(EllipticAuth.SECRET, EllipticAuth.TIME_OF_REQUEST_IN_MS, \"POST\", \"/v2/analyses\", EXAMPLE_PAYLOAD));\n // 65mQHB2o95lL3I+N/bZYwDC9p2YvNwsVDnXr8u72hUk=\n\n // Example Two: GET with empty payload\n Console.WriteLine(get_signature(EllipticAuth.SECRET, EllipticAuth.TIME_OF_REQUEST_IN_MS, \"GET\", \"/v2/customers\", \"{}\"));\n // cN9fRUqeT7UnwwpkBZaNmnwxKAPHkhytdXelfUVvxMI=\n return;\n }\n}\n```\n\n
\n\n
PHP\n\n```php\n/**\n* Generate a signature for use when signing a request to the API\n*\n* @param $secret your secret supplied by Elliptic - a base64 encoded string\n* @param $time_of_request current time, in milliseconds, since 1 Jan 1970 00:00:00 UTC\n* @param $http_method must be uppercase\n* @param $http_path API endpoint including query string\n* @param $payload string encoded JSON object or '{}' if there is no request body\n*/\nfunction get_signature(\n $secret,\n $time_of_request,\n $http_method,\n $http_path,\n $payload\n) {\n // create a SHA256 HMAC using the supplied secret, decoded from base64\n $ctx = hash_init('sha256', HASH_HMAC, base64_decode($secret));\n // concatenate the request text to be signed\n $request_text = $time_of_request . $http_method . $http_path . $payload;\n // update the HMAC with the text to be signed\n hash_update($ctx, $request_text);\n // output the signature as a base64 encoded string\n return base64_encode(hex2bin(hash_final($ctx)));\n}\n\n$SECRET = '894f142d667e8cdaca6822ac173937af'; // Supplied by Elliptic\n// Disclaimer: this secret is just an example\n$TIME_OF_REQUEST_IN_MS = 1478692862000; // For real world use something like (int)(microtime(true)*1000)\n\n$EXAMPLE_PAYLOAD = [[\n \"customer_reference\" => \"123456\",\n \"subject\" => [\n \"asset\" => \"BTC\",\n \"hash\" => \"accf5c09cc027339a3beb2e28104ce9f406ecbbd29775b4a1a17ba213f1e035e\",\n \"output_address\" => \"15Hm2UEPaEuiAmgyNgd5mF3wugqLsYs3Wn\",\n \"output_type\" => \"address\",\n \"type\" => \"transaction\"\n ],\n \"type\" => \"source_of_funds\"\n ]];\n\n// Example One: POST with payload - you only need to run json_encode when passing a request body\necho get_signature($SECRET, $TIME_OF_REQUEST_IN_MS, 'POST', '/v2/analyses', json_encode($EXAMPLE_PAYLOAD)) . \"\\n\";\n// 65mQHB2o95lL3I+N/bZYwDC9p2YvNwsVDnXr8u72hUk=\n\n// Example Two: GET with empty payload - do not run json_encode with no request body, pass an empty object as string\necho get_signature($SECRET, $TIME_OF_REQUEST_IN_MS, 'GET', '/v2/customers', '{}') . \"\\n\";\n// cN9fRUqeT7UnwwpkBZaNmnwxKAPHkhytdXelfUVvxMI=\n```\n\n
\n\n
Golang\n\n```go\npackage main\n\nimport (\n \"crypto/hmac\"\n \"crypto/sha256\"\n \"encoding/base64\"\n \"fmt\"\n \"log\"\n \"strconv\"\n \"strings\"\n)\n\n// Generate a signature for use when signing a request to the API\n// - secret: your secret supplied by Elliptic - a base64 encoded string\n// - time_of_request: current time, in milliseconds, since 1 Jan 1970 00:00:00 UTC\n// - http_method: must be uppercase\n// - http_path: API endpoint including query string\n// - payload: string encoded JSON object or '{}' if there is no request body\nfunc get_signature(secret string, time_of_request int64, http_method string, http_path string, payload string) string {\n // create a SHA256 HMAC using the supplied secret, decoded from base64\n ds, err := base64.StdEncoding.DecodeString(secret)\n if err != nil {\n log.Fatal(\"error:\", err)\n }\n h := hmac.New(sha256.New, []byte(ds))\n\n // concatenate the request text to be signed\n request_text := strconv.FormatInt(time_of_request, 10) + http_method + strings.ToLower(http_path) + payload\n\n // update the HMAC with the text to be signed\n h.Write([]byte(request_text))\n\n // output the signature as a base64 encoded string\n return base64.StdEncoding.EncodeToString([]byte(h.Sum(nil)))\n}\n\nfunc main() {\n secret := \"894f142d667e8cdaca6822ac173937af\" // Supplied by Elliptic\n // Disclaimer: this secret is just an example\n time_of_request_in_ms := int64(1478692862000) // For real world use time.Now().UnixMilli()\n\n example_payload := `[{\"customer_reference\":\"123456\",\"subject\":{\"asset\":\"BTC\",\"hash\":\"accf5c09cc027339a3beb2e28104ce9f406ecbbd29775b4a1a17ba213f1e035e\",\"output_address\":\"15Hm2UEPaEuiAmgyNgd5mF3wugqLsYs3Wn\",\"output_type\":\"address\",\"type\":\"transaction\"},\"type\":\"source_of_funds\"}]`\n\n // Example One: POST with payload - you only need to run stringify json when passing a request body\n fmt.Println(get_signature(secret, time_of_request_in_ms, \"POST\", \"/v2/analyses\", example_payload))\n // 65mQHB2o95lL3I+N/bZYwDC9p2YvNwsVDnXr8u72hUk=\n\n // Example Two: GET with empty payload - do not run stringify with no request body, pass an empty object as string\n fmt.Println(get_signature(secret, time_of_request_in_ms, \"GET\", \"/v2/customers\", `{}`))\n // cN9fRUqeT7UnwwpkBZaNmnwxKAPHkhytdXelfUVvxMI=\n}\n```\n\n
\n\n
Postman\n\n```javascript\n/**\n* This function will create signature on the base of API_SECRET\n* variable set in Postman. Use it as pre-request script in Postman\n*/\nfunction makeSignature() {\n const timestamp = Date.now();\n // This regex assumes that the URLs you are using in postman have the hostname templated\n // like {{AML_API_HOST}}/your/url so won’t work if instead you are using the AML API host set directly in the URL bar\n const pathRegex = /(?:{{[^}]*}})(.*$)/g;\n const match = pathRegex.exec(request.url);\n const path = /\\?$/.test(match[1]) ? match[1].substring(0, match[1].length - 1) : match[1];\n const strBody = (typeof request.data == 'object' ? '{}' : JSON.stringify(JSON.parse(request.data)));\n const text = timestamp + request.method.toUpperCase() + path.toLowerCase() + strBody;\n const key = CryptoJS.enc.Base64.parse(pm.variables.get(\"API_SECRET\"));\n const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.SHA256, key);\n hmac.update(text);\n const signature = CryptoJS.enc.Base64.stringify(hmac.finalize());\n return [signature, timestamp];\n}\n\nvar sig = makeSignature();\n\n// These variables should be consumed in the request header configuration\npostman.setGlobalVariable('REQ_SIGNATURE', sig[0]);\npostman.setGlobalVariable(\"REQ_TIMESTAMP\", sig[1]);\npostman.setGlobalVariable(\"REQ_DATA\", typeof request.data);\n```\n\n
\n\n### Debugging Authentication\nThe `WWW-Authenticate` response header gives useful information to help understand what's going wrong:\n - `error_description=\"invalid signature\"`: The signature generated by your code does not match what we've generated on the API\n - `error_description=\"invalid timestamp 1605268999252\"`: The timestamp you've provided is invalid, it should be milliseconds since epoch\n - No error discription usually indicates that the key you're using is invalid\n" servers: - url: https://aml-api.elliptic.co/v2 description: Production security: - oauth2: - openid - profile - apiKey: [] signature: [] timestamp: [] tags: - name: Customers description: 'The customers endpoints are used to manage customers which you have associated analysis with, via the `customer_reference` analysis subject attribute. Currently, the customers endpoints only refer to customers with [Transaction Analysis](#tag/Transaction-Analyses). Customers with only [Wallet Analysis](#tag/Wallet-Analyses) will not yield responses from these endpoints ' paths: /customers: get: tags: - Customers summary: Paginate all customers description: 'Paginate through all customers, with various sort/filter parameters. ' parameters: - name: page in: query description: requested page number schema: type: integer format: int32 minimum: 1 default: 1 - name: per_page in: query description: number of items per page schema: type: integer format: int32 minimum: 1 maximum: 100 default: 20 - name: workflow_status description: workflow status filter in: query schema: type: array items: type: string enum: - active - archived - name: keyword in: query description: filter both label and reference (case insensitive). Whitespace implies AND. schema: type: string minLength: 1 - name: label in: query description: filter by label name schema: type: string minLength: 1 - name: label_matches in: query description: filter by exact label name (case sensitive) schema: type: string minLength: 1 - name: investigator_id description: filter by these ids of users assigned to investigate the customer (can be empty string to get unassigned) in: query schema: type: array items: type: string format: NullableUUIDv4 - name: customer_label_id in: query description: label filter (if empty string then customers with no labels are returned) schema: type: array items: type: string format: NullableUUIDv4 - name: reference_contains in: query description: a string on which to match customer references schema: type: string minLength: 1 - name: reference_matches in: query description: 'Deprecated Use reference instead ' schema: type: string minLength: 1 - name: reference in: query description: a customer reference to match (exactly) schema: type: string minLength: 1 - name: expand in: query description: to get more detailed labels object schema: type: array items: type: string enum: - labels default: [] - name: sort description: sorting criterion in: query schema: type: string enum: - last_queried - -last_queried - reference - -reference - first_tx_time - -first_tx_time - last_tx_time - -last_tx_time - max_score - -max_score - avg_score - -avg_score - total_volume - -total_volume - total_volume_usd - -total_volume_usd - workflow_status - -workflow_status default: -last_queried - name: minimum_avg_score description: filter by avg_score >= the given value in: query schema: type: number format: float - name: maximum_avg_score description: filter by avg_score <= the given value in: query schema: type: number format: float - name: minimum_max_score description: filter by max_score >= the given value in: query schema: type: number format: float - name: maximum_max_score description: filter by max_score <= the given value in: query schema: type: number format: float - name: minimum_vol description: filter by volume >= the given value in USD in: query schema: type: number format: float - name: maximum_vol description: filter by volume <= the given value in USD in: query schema: type: number format: float - name: first_tx_before description: filter the earliest transaction time of this customer (ISO8601 datetime) <= the given date in: query schema: type: string format: date-time example: '2030-02-26T14:33:34.787Z' - name: first_tx_after description: filter the earliest transaction time of this customer (ISO8601 datetime) >= the given date in: query schema: type: string format: date-time example: '2010-02-26T14:33:34.787Z' - name: last_tx_before description: filter the latest transaction time of this customer (ISO8601 datetime) <= the given date in: query schema: type: string format: date-time example: '2030-02-26T14:33:34.787Z' - name: last_tx_after description: filter the latest transaction time of this customer (ISO8601 datetime) >= the given date in: query schema: type: string format: date-time example: '2010-02-26T14:33:34.787Z' - name: last_queried_before description: filter last_queried time of customer (ISO8601 datetime) <= the given date in: query schema: type: string format: date-time example: '2030-02-26T14:33:34.787Z' - name: last_queried_after description: filter last_queried time of customer (ISO8601 datetime) >= the given date in: query schema: type: string format: date-time example: '2010-02-26T14:33:34.787Z' - name: status description: allowed statuses in: query schema: type: array items: type: string enum: - complete - processing - error minItems: 1 - name: include_no_risk description: 'Deprecated No longer supported ' in: query schema: type: boolean default: true - name: isMc description: 'Deprecated This will always evaluate to true. Legacy mode no longer supported ' in: query example: true schema: type: boolean default: true responses: '200': description: Customers successfully retrieved content: application/json: schema: type: object properties: page: type: number format: int32 minimum: 0 example: 5 per_page: type: number format: int32 minimum: 0 example: 5 total_pages: type: number format: int32 minimum: 0 example: 5 total_items: description: total number of items on all pages type: integer format: int32 example: 1 items: description: array of retrieved customers type: array items: $ref: '#/components/schemas/Customer' additionalProperties: false '400': description: Bad request, e.g. invalid `workflow_status` in query. content: application/json: schema: $ref: '#/components/schemas/BadRequestError' '401': $ref: '#/components/responses/401' '403': $ref: '#/components/responses/403' '500': $ref: '#/components/responses/500' /customers/{customer_id}: get: tags: - Customers summary: Get customer by ID description: 'Get a single customer by ID ' parameters: - name: customer_id description: uuid identifier of a customer in: path required: true schema: type: string format: UUIDv4 - name: expand description: to expand customer labels and/or statistics in: query schema: type: array items: type: string enum: - labels - statistics default: [] - name: isMc description: 'Deprecated This will always evaluate to true. Legacy mode no longer supported ' example: true in: query schema: type: boolean default: true responses: '200': description: Customer successfully retrieved content: application/json: schema: $ref: '#/components/schemas/Customer' '401': $ref: '#/components/responses/401' '403': $ref: '#/components/responses/403' '404': description: Customer ID not found content: application/json: schema: $ref: '#/components/schemas/NotFoundError' '500': $ref: '#/components/responses/500' /customers/investigator: post: tags: - Customers summary: Bulk update investigator ID description: 'The customers & investigator_id must all exist & belong to the team of the authenticated user. ' requestBody: content: application/json: schema: type: object properties: customer_ids: description: the customer id(s) to update type: array minItems: 1 items: type: string format: UUIDv4 example: 61dc6f9c-fb4b-4aad-966e-801ea4caa7d6 investigator_id: description: new investigator id (can be null). Replaces old. format: NullableUUIDv4 example: 801a54af-6bbe-48db-ab28-7edc343649d5 nullable: true type: string required: - customer_ids - investigator_id responses: '204': description: Customers successfully updated '400': description: Bad request, e.g. invalid `customer_id` content: application/json: schema: $ref: '#/components/schemas/BadRequestError' '401': $ref: '#/components/responses/401' '403': $ref: '#/components/responses/403' '500': $ref: '#/components/responses/500' /customers/workflow_status: post: tags: - Customers summary: Bulk update workflow status description: 'The customers must all exist & belong to the team of the authenticated user. Currently the only states are ''Active'' and ''Archived''. Transitions both ways are allowed. ' requestBody: content: application/json: schema: type: object properties: customer_ids: description: the customer id(s) to update type: array minItems: 1 items: type: string format: UUIDv4 example: - 61dc6f9c-fb4b-4aad-966e-801ea4caa7d6 - 55f2bf9c-ff91-4146-a9d7-1d484d1dbac5 workflow_status: description: new workflow status ('active', 'archived'). Replaces old. type: string enum: - active - archived waitForSearchSync: type: boolean description: if false, skip waiting for the analysis search index to update required: - customer_ids responses: '204': description: Customer successfully updated '400': description: Bad request, e.g. invalid `customer_ids` content: application/json: schema: $ref: '#/components/schemas/BadRequestError' '401': $ref: '#/components/responses/401' '403': $ref: '#/components/responses/403' '500': $ref: '#/components/responses/500' /copilot/customers/summary/{customerId}: get: tags: - Customers summary: Get customer summary description: Returns a summary for a customer by ID. The shape of the response depends on the `request` query parameter. operationId: getCustomerSummary parameters: - name: customerId in: path required: true description: Customer ID (UUID) schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: request in: query required: false description: Type of summary to return. Omit for a full structured summary with analysis IDs. `overview`, `wallet`, or `transaction` return an LLM narrative. `wallet_ids` or `transaction_ids` return the highest-risk items with their hash and analysis ID. schema: type: string enum: - overview - wallet - transaction - wallet_ids - transaction_ids example: overview responses: '200': description: 'Customer summary. Shape depends on `request` query param: `SummaryResult` when omitted, LLM narrative string for `overview`/`wallet`/`transaction`, or `MaxRiskTrigger[]` for `wallet_ids`/`transaction_ids`.' content: text/plain: schema: type: string description: LLM-generated narrative (returned when request is overview, wallet, or transaction) application/json: schema: oneOf: - $ref: '#/components/schemas/SummaryResult' - type: array items: $ref: '#/components/schemas/MaxRiskTrigger' description: Highest-risk items with hash and analysisId (returned when request is wallet_ids or transaction_ids) '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /copilot/customers/summary/{customerId}/transaction-assets: get: tags: - Customers summary: Get customer transaction assets description: Returns an AI-generated analysis of transaction assets for a customer operationId: getTransactionAssetsByCustomerId parameters: - name: customerId in: path required: true description: Customer ID (UUID) schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 responses: '200': description: AI-generated transaction assets analysis content: text/plain: schema: type: string description: LLM-generated narrative about the customer's transaction assets '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' components: schemas: UnauthorizedError: type: string example: Unauthorized ErrorResponse: type: object properties: message: type: string description: Error message statusCode: type: integer description: HTTP status code timestamp: type: string format: date-time description: Timestamp of the error context: type: object additionalProperties: true description: Additional error context required: - message - statusCode - timestamp SummaryResult: type: object description: Full customer summary including an LLM narrative and the analysis IDs used to generate it properties: summary: type: string description: LLM-generated customer summary analysisIds: type: object properties: wallets: type: array items: type: string format: uuid description: Wallet analysis IDs used in the summary transactions: type: array items: type: string format: uuid description: Transaction analysis IDs used in the summary required: - wallets - transactions required: - summary - analysisIds MaxRiskTrigger: type: object description: A wallet or transaction at the maximum risk score, with its hash and analysis ID properties: hash: type: string description: Wallet address or transaction hash analysisId: type: string format: uuid description: Analysis ID (UUID) required: - hash - analysisId BadRequestError: type: object properties: name: type: string example: BadRequestError message: type: string example: Invalid id parameter Customer: type: object title: Customer description: an object representing a single Customer properties: id: description: UUIDv4 identifier of the customer type: string format: UUIDv4 example: b7535048-76f8-4f60-bdd3-9d659298f9e7 updated_at: description: ISO date time with time zone (UTC) of when the customer was last updated type: string format: date-time example: '2015-05-13T10:36:21.000Z' created_at: description: Customer's creation's ISO date time with time zone (UTC) type: string format: date-time example: '2015-05-13T10:36:21.000Z' team_id: description: UUIDv4 identifier of the team the user belongs to type: string format: UUIDv4 example: e333694b-c7c7-4a36-bf35-ed2615865242 investigator_id: description: UUIDv4 identifier of the user assigned to investigate customer format: NullableUUIDv4 example: b68704ce-7aaa-45b1-996d-8d760657baf2 reference: description: used by our client to refer to this customer type: string example: Investigator labels: description: list of labels applied to the customer. If not expanded then just array of {"id":// uuid, label ids } type: array items: oneOf: - type: string - type: object example: - id: 2118a3ca-a1c0-4f81-92b1-a1069d6a8fb9 team_id: 15f2b16d-f825-4d40-8db3-f4e080f90759 name: Parent Label parent_customer_label_id: null status: description: customer's status type: string enum: - processing - error - complete example: complete is_processing: description: true if a query on this customer currently processing type: boolean example: false has_errors: description: true if a query on this customer has errored type: boolean example: true last_queried: description: ISO date time with time zone (UTC) of when the last query was run type: string format: date-time example: '2015-05-13T10:36:21.000Z' workflow_status: type: string description: the workflow status enum: - active - archived statistics: type: object title: CustomerStats description: an object representing aggregated statistics for a single customer properties: first_tx_time: description: ISO date time with time zone (UTC) of the transaction having the smallest transaction time format: date-time example: '2015-05-13T10:36:21.000Z' nullable: true type: string last_tx_time: description: ISO date time with time zone (UTC) of the transaction having the biggest transaction time format: date-time example: '2015-05-13T10:36:21.000Z' nullable: true type: string avg_score: description: average between all this customer's transactions risks format: float example: 3.3 nullable: true type: number max_score: description: the highest risk of this customer's transactions risks format: float example: 9 nullable: true type: number min_score: description: the minimum risk of this customer's transactions risks format: float example: 9 nullable: true type: number total_volume: description: sum of all customer transactions output satoshis format: int64 example: 993842398342 nullable: true type: number total_fees: description: sum of all customer transactions fees format: int64 example: 993842398342 nullable: true type: number customer_tx_count: description: total number of customer_txs that this customer has format: int32 example: 900800 nullable: true type: number deposits: $ref: '#/components/schemas/CustomerTxInfo' withdrawals: $ref: '#/components/schemas/CustomerTxInfo' min_output_satoshis: description: the lowest volume of this customer's transactions volumes example: 993842398342 nullable: true type: number max_output_satoshis: description: the highest volume of this customer's transactions volumes example: 993842398342 nullable: true type: number avg_output_satoshis: description: average of this customer's transactions volumes example: 993842398342 nullable: true type: number required: - first_tx_time - last_tx_time - avg_score - max_score - total_volume - customer_tx_count - deposits - withdrawals - min_output_satoshis - max_output_satoshis - avg_output_satoshis additionalProperties: false required: - id - team_id - reference - last_queried - workflow_status additionalProperties: false CustomerTxInfo: type: object title: customerTxInfo description: an object representing a single analysed Transaction attached to a Customer properties: total_volume: description: Sum of all transactions' volume example: 993842398342 nullable: true type: number customer_tx_count: description: Number of transactions format: int64 example: 3341 nullable: true type: number total_fees: description: sum of all customer transactions fees format: int64 example: 993842398342 nullable: true type: number required: - total_volume - customer_tx_count additionalProperties: false Error: type: object properties: name: type: string example: GenericErrorName message: type: string example: Something bad happened. NotFoundError: type: object properties: name: type: string example: NotFoundError message: type: string example: Entity not found. ForbiddenError: type: object properties: name: type: string example: ForbiddenError message: type: string example: You do not have enough privilege to access this path. responses: Forbidden: description: Forbidden - Insufficient permissions content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: message: Forbidden - Insufficient permissions statusCode: 403 timestamp: '2024-01-01T00:00:00.000Z' Unauthorized: description: Unauthorized - Missing or invalid authentication token content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: message: Unauthorized statusCode: 401 timestamp: '2024-01-01T00:00:00.000Z' NotFound: description: Resource not found content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: message: Resource not found statusCode: 404 timestamp: '2024-01-01T00:00:00.000Z' InternalServerError: description: Internal server error content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: message: Internal server error statusCode: 500 timestamp: '2024-01-01T00:00:00.000Z' '403': description: Not authorized content: application/json: schema: $ref: '#/components/schemas/ForbiddenError' BadRequest: description: Bad request - Invalid input parameters content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: message: Validation failed statusCode: 400 timestamp: '2024-01-01T00:00:00.000Z' context: errors: - Customer ID must be a valid UUID '401': description: Not authenticated content: application/json: schema: $ref: '#/components/schemas/UnauthorizedError' '500': description: Server Error content: application/json: schema: $ref: '#/components/schemas/Error' securitySchemes: oauth2: type: oauth2 flows: authorizationCode: authorizationUrl: https://login.elliptic.co/authorize tokenUrl: https://login.elliptic.co/oauth/token scopes: openid: '' profile: '' apiKey: description: API Key type: apiKey in: header name: x-access-key signature: description: (Request Time, HTTP Method, Lowercase Path, Request Payload) signed with API Secret type: apiKey in: header name: x-access-sign timestamp: type: apiKey in: header name: x-access-timestamp x-readme: oauth-options: usePkce: true x-tagGroups: - name: Navigator tags: - Transaction Analyses - name: Lens tags: - Wallet Analyses - name: Workflow Management tags: - Customers - Transaction Workflow - name: Account Management tags: - Users - name: Asset Information tags: - Assets table