> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developer.jelliu.co/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developer.jelliu.co/_mcp/server.

# Retrieve a country's default compliance rules

GET https://api.jelliu.co/api/compliance/defaults/{countryCode}

Returns Jelliu's built-in compliance defaults for a country. They are the same for every workspace, and they
apply whenever the workspace has not saved a config for that country. The response also includes
`ai_disclosure_note`, which explains why the AI-disclosure default is what it is. It is `false` for every
country, because no national duty that binds private businesses has been found. Some state and foreign rules
(California, Colorado, the EU) may still apply to you.

Use it as the starting point for `PUT /api/compliance/configs`.

**Consistency.** Static data. Responses carry `Cache-Control: public, max-age=86400`.

**Access**
- **Required scope:** `read` or `write`. Any signed-in workspace role.
- **Rate limit:** General API — 120 (Starter), 200 (Growth), 300 (Business) or 600 (Enterprise) requests/min per workspace. See [Rate limits](/rate-limits).
- **Plan:** Available on every plan.


Reference: https://developer.jelliu.co/api-reference/compliance/get-compliance-defaults-by-country-code

## Authentication

- `Authorization` header (bearer token, required) — Workspace API key: `jl_` followed by 64 lowercase hex characters, created by the workspace owner in the dashboard (**Settings → API Keys**) and sent as `Authorization: Bearer jl_...`. The plaintext is shown once, at creation; Jelliu stores only a SHA-256 hash. A workspace can hold up to 25 active keys. | Scope | GET / HEAD | POST / PUT / PATCH / DELETE | Admin-only routes | | --- | --- | --- | --- | | `read` | Yes | No | No | | `write` | Yes | Yes | No | | `full` | Yes | Yes | Yes | Operations restricted to admins or owners reject keys without the `full` scope with `403`, and say so in their description. No key, whatever its scope, can mint or revoke API keys or rotate a webhook secret — that requires a signed-in owner session. A revoked key stops authenticating within about 10 seconds. See [Authentication](/authentication).

## Request

### Path parameters

- `countryCode` (enum, required) — ISO 3166-1 alpha-2 code of a supported country, in upper case (`CO`, `MX`, `BR`, `US`, `AR`, `CL`, `PE`). Any other value returns 400.
  - Allowed values: `CO`, `MX`, `BR`, `US`, `AR`, `CL`, `PE`

## Response

### 200

Country defaults.

- `data` (object, required) — Jelliu's built-in rules for one country. They apply when the workspace has no saved config for it.
  - `require_recording_consent` (boolean, optional) — Whether calls open with a recording notice. `true` for every supported country.
  - `consent_script` (string, optional, nullable) — Default recording notice, in the country's language (Spanish, Portuguese for BR, English for US).
  - `require_dnc_check` (boolean, optional) — Whether contacts marked do-not-call are blocked. `true` for every supported country.
  - `max_call_attempts_per_day` (integer, optional) — Default maximum calls to one contact per local day.
  - `max_call_attempts_total` (integer, optional) — Default maximum lifetime attempts (BR 8, US 15, others 10).
  - `allowed_call_hours` (object, optional) — The local window in which outreach is allowed. `start` is inclusive and `end` is exclusive. When `start` is later than `end`, the window wraps past midnight. At check time, the workspace's own time zone setting, when present, replaces `timezone`.
    - `start` (string, required) — Window start, `HH:MM`, 24-hour clock.
    - `end` (string, required) — Window end, `HH:MM`, 24-hour clock, exclusive.
    - `timezone` (string, required) — IANA time zone name. Unknown names fail validation with `Invalid IANA timezone`.
  - `data_retention_days` (integer, optional) — Default retention in days (US 730, others 365).
  - `require_opt_in` (boolean, optional) — Whether the country is flagged as requiring prior opt-in (`true` for CO and BR). Informational.
  - `blocked_prefixes` (list of string, optional) — Default blocked prefixes. Empty for every supported country.
  - `require_ai_disclosure` (boolean, optional) — Default AI-disclosure notice. `false` for every supported country.
  - `ai_disclosure_note` (string, optional, nullable) — Why the AI-disclosure default is what it is for this country, including state or foreign rules to consider.

## Errors

### 400 Bad Request Error

`countryCode` is not one of the supported countries.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 401 Unauthorized Error

No usable credential. Either the `Authorization` header is missing or is not a well-formed `Bearer jl_…` key, or the key is unknown, revoked or expired. Do not retry with the same key. See [Authentication](/authentication#401-unauthorized).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 403 Forbidden Error

The API key has neither `read` nor `write`, or the workspace is suspended.

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

### 429 Too Many Requests Error

Rate limit exceeded for the current 60-second window (`RATE_LIMIT_EXCEEDED`). Wait `Retry-After` seconds, then retry. The `message` differs per limiter; the code does not. See [Rate limits](/rate-limits).

- `error` (object, required) — The error object. Always has `code` and `message`.
  - `code` (string, required) — Stable machine-readable error code (for example `VALIDATION_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `BILLING_ERROR`, `COMPLIANCE_BLOCKED`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`). Switch on this, not on `message`. See [Errors](/errors).
  - `message` (string, required) — Human-readable explanation. English or Spanish depending on the route; may change without notice.
  - `details` (object or list of object, optional) — Present on `VALIDATION_FAILED` only. Its shape depends on how the route validates: * a field map, either Zod's `flatten()` output (`{ "formErrors": [], "fieldErrors": { "name": ["..."] } }`) or just its `fieldErrors` part (`{ "name": ["..."] }`); * an issue list, where each issue has at least `path`, `message` and `code`. `path` is a dot-separated string on routes that let the schema throw, and an array of keys on routes that forward Zod's raw issues (those also carry Zod's extra issue fields).
    - Field map
      - `formErrors` (list of string, optional)
      - `fieldErrors` (map from string to list of string, optional)
  - `metadata` (map from string to any, optional) — Structured detail exposed for a small allowlist of codes only — for example `BILLING_ERROR` carries `limit`, `current` and `tier` (resource caps) or `tier` and `feature` (feature gates).

## Examples

### Colombia (CO)

**Response**

```json
{
  "data": {
    "require_recording_consent": true,
    "consent_script": "Esta llamada será grabada con fines de calidad y cumplimiento según la Ley 1581 de Protección de Datos Personales. ¿Autoriza continuar?",
    "require_dnc_check": true,
    "max_call_attempts_per_day": 3,
    "max_call_attempts_total": 10,
    "allowed_call_hours": {
      "start": "08:00",
      "end": "20:00",
      "timezone": "America/Bogota"
    },
    "data_retention_days": 365,
    "require_opt_in": true,
    "blocked_prefixes": [],
    "require_ai_disclosure": false,
    "ai_disclosure_note": "No specific AI-disclosure duty found. Ley 1581 governs data and recording, which is a separate switch."
  }
}
```

**SDK Code**

```python Colombia (CO)
import requests

url = "https://api.jelliu.co/api/compliance/defaults/BR"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript Colombia (CO)
const url = 'https://api.jelliu.co/api/compliance/defaults/BR';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Colombia (CO)
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/api/compliance/defaults/BR"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Colombia (CO)
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/api/compliance/defaults/BR")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java Colombia (CO)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.jelliu.co/api/compliance/defaults/BR")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php Colombia (CO)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.jelliu.co/api/compliance/defaults/BR', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp Colombia (CO)
using RestSharp;

var client = new RestClient("https://api.jelliu.co/api/compliance/defaults/BR");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift Colombia (CO)
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/api/compliance/defaults/BR")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```

### United States (US)

**Response**

```json
{
  "data": {
    "require_recording_consent": true,
    "consent_script": "This call may be recorded for quality assurance and compliance purposes under the TCPA. Do you consent to continue?",
    "require_dnc_check": true,
    "max_call_attempts_per_day": 3,
    "max_call_attempts_total": 15,
    "allowed_call_hours": {
      "start": "08:00",
      "end": "21:00",
      "timezone": "America/New_York"
    },
    "data_retention_days": 730,
    "require_opt_in": false,
    "blocked_prefixes": [],
    "require_ai_disclosure": false,
    "ai_disclosure_note": "No federal duty. California AB 2905 (since 2025-01-01) requires an AI-voice call to say so at the start; Colorado's AI framework (since 2026-05-30) requires clear notice at the point of interaction. Texas's duty applies to government agencies and healthcare, NOT to private businesses. Because these are state rules and the country is resolved from the +1 prefix, turn this on per agent if the agent calls California or Colorado."
  }
}
```

**SDK Code**

```python United States (US)
import requests

url = "https://api.jelliu.co/api/compliance/defaults/BR"

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers)

print(response.json())
```

```javascript United States (US)
const url = 'https://api.jelliu.co/api/compliance/defaults/BR';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go United States (US)
package main

import (
	"fmt"
	"net/http"
	"io"
)

func main() {

	url := "https://api.jelliu.co/api/compliance/defaults/BR"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby United States (US)
require 'uri'
require 'net/http'

url = URI("https://api.jelliu.co/api/compliance/defaults/BR")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

response = http.request(request)
puts response.read_body
```

```java United States (US)
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.get("https://api.jelliu.co/api/compliance/defaults/BR")
  .header("Authorization", "Bearer <token>")
  .asString();
```

```php United States (US)
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.jelliu.co/api/compliance/defaults/BR', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

echo $response->getBody();
```

```csharp United States (US)
using RestSharp;

var client = new RestClient("https://api.jelliu.co/api/compliance/defaults/BR");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift United States (US)
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.jelliu.co/api/compliance/defaults/BR")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```