> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zavu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# URL Verification

> Pre-verify URLs before sending SMS or email messages containing links

URL verification ensures that links in your SMS and email messages are safe and compliant. All URLs in SMS and email messages must be verified before the message can be sent.

## Why URL Verification?

Carriers and email providers actively filter messages containing malicious or suspicious links. URL verification helps you:

* **Prevent delivery failures** - Messages with unverified or blocked URLs won't be delivered
* **Protect your reputation** - Avoid being flagged as a spam sender
* **Ensure compliance** - Meet carrier requirements for SMS messaging
* **Block malicious links** - Automatic detection of phishing and malware sites

## How It Works

<Steps>
  <Step title="Extract URLs">
    When you send a message, Zavu extracts all URLs from the content
  </Step>

  <Step title="Check Verification Status">
    Each URL is checked against your verified URLs list
  </Step>

  <Step title="Automatic Verification">
    New URLs are automatically verified against Google Web Risk API
  </Step>

  <Step title="Delivery Decision">
    Safe URLs are approved; malicious URLs block the message
  </Step>
</Steps>

## URL Verification Status

| Status      | Description                          |
| ----------- | ------------------------------------ |
| `pending`   | URL submitted, awaiting verification |
| `approved`  | URL verified as safe                 |
| `rejected`  | URL rejected by security team        |
| `malicious` | URL flagged as potentially harmful   |

## Pre-Verifying URLs

Submit URLs for verification before using them in messages:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Zavudev from '@zavudev/sdk';

  const zavu = new Zavudev();

  const result = await zavu.urls.submit({
    url: "https://mystore.example.com/sale",
  });

  console.log(`URL ID: ${result.url.id}`);
  console.log(`Status: ${result.url.status}`); // "approved" for safe URLs
  console.log(`Domain: ${result.url.domain}`);
  ```

  ```python Python theme={null}
  from zavudev import Zavudev

  zavu = Zavudev()

  result = zavu.urls.submit(
      url="https://mystore.example.com/sale"
  )

  print(f"URL ID: {result.url.id}")
  print(f"Status: {result.url.status}")  # "approved" for safe URLs
  print(f"Domain: {result.url.domain}")
  ```

  ```ruby Ruby theme={null}
  require "zavudev"

  client = Zavudev::Client.new(api_key: ENV["ZAVUDEV_API_KEY"])

  result = client.urls.submit(
    url: "https://mystore.example.com/sale"
  )

  puts "URL ID: #{result.url.id}"
  puts "Status: #{result.url.status}" # "approved" for safe URLs
  puts "Domain: #{result.url.domain}"
  ```

  ```go Go theme={null}
  client := zavudev.NewClient(zavudev.WithAPIKey(os.Getenv("ZAVUDEV_API_KEY")))

  result, _ := client.URLs.Submit(context.TODO(), zavudev.URLSubmitParams{
  	URL: zavudev.String("https://mystore.example.com/sale"),
  })

  fmt.Printf("URL ID: %s\n", result.URL.ID)
  fmt.Printf("Status: %s\n", result.URL.Status) // "approved" for safe URLs
  fmt.Printf("Domain: %s\n", result.URL.Domain)
  ```

  ```php PHP theme={null}
  $client = new Zavudev\Client(apiKey: getenv('ZAVUDEV_API_KEY'));

  $result = $client->urls->submit([
      'url' => 'https://mystore.example.com/sale',
  ]);

  echo "URL ID: {$result->url->id}\n";
  echo "Status: {$result->url->status}\n"; // "approved" for safe URLs
  echo "Domain: {$result->url->domain}\n";
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.zavu.dev/v1/urls \
    -H "Authorization: Bearer $ZAVU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://mystore.example.com/sale"
    }'
  ```
</CodeGroup>

### Approved Response

```json theme={null}
{
  "url": {
    "id": "url_abc123",
    "url": "https://mystore.example.com/sale",
    "domain": "mystore.example.com",
    "status": "approved",
    "approvalType": "auto_web_risk",
    "createdAt": "2024-01-15T10:30:00.000Z"
  }
}
```

## Blocked URLs

### URL Shorteners

URL shorteners are **always blocked** because they hide the destination URL:

```bash theme={null}
# These will be rejected
bit.ly/abc123
t.co/xyz789
tinyurl.com/short
```

<Warning>
  URL shorteners like bit.ly, t.co, goo.gl, tinyurl, and ow\.ly are not allowed. Always use the full destination URL in your messages.
</Warning>

### Malicious URLs

URLs flagged by Google Web Risk as malicious are automatically blocked:

```json theme={null}
{
  "code": "url_malicious",
  "message": "URL has been flagged as potentially harmful by security systems",
  "details": {
    "urls": ["https://malicious-site.example"],
    "threatTypes": ["MALWARE", "SOCIAL_ENGINEERING"],
    "warning": "fraud_prevention"
  }
}
```

<Note>
  Attempts to send messages with malicious URLs are logged and may result in account review for repeated violations.
</Note>

## Listing Verified URLs

View all URLs that have been verified for your project:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const { items, nextCursor } = await zavu.urls.list({
    status: "approved",
    limit: 50,
  });

  for (const url of items) {
    console.log(`${url.domain}: ${url.url} (${url.status})`);
  }
  ```

  ```python Python theme={null}
  result = zavu.urls.list(
      status="approved",
      limit=50
  )

  for url in result.items:
      print(f"{url.domain}: {url.url} ({url.status})")
  ```

  ```ruby Ruby theme={null}
  result = client.urls.list(
    status: "approved",
    limit: 50
  )

  result.items.each do |url|
    puts "#{url.domain}: #{url.url} (#{url.status})"
  end
  ```

  ```go Go theme={null}
  result, _ := client.URLs.List(context.TODO(), zavudev.URLListParams{
  	Status: zavudev.String("approved"),
  	Limit:  zavudev.Int(50),
  })

  for _, url := range result.Items {
  	fmt.Printf("%s: %s (%s)\n", url.Domain, url.URL, url.Status)
  }
  ```

  ```php PHP theme={null}
  $result = $client->urls->list([
      'status' => 'approved',
      'limit' => 50,
  ]);

  foreach ($result->items as $url) {
      echo "{$url->domain}: {$url->url} ({$url->status})\n";
  }
  ```

  ```bash cURL theme={null}
  curl "https://api.zavu.dev/v1/urls?status=approved&limit=50" \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "items": [
    {
      "id": "url_abc123",
      "url": "https://mystore.example.com/sale",
      "domain": "mystore.example.com",
      "status": "approved",
      "approvalType": "auto_web_risk",
      "createdAt": "2024-01-15T10:30:00.000Z"
    },
    {
      "id": "url_def456",
      "url": "https://mystore.example.com/products",
      "domain": "mystore.example.com",
      "status": "approved",
      "approvalType": "auto_web_risk",
      "createdAt": "2024-01-15T10:25:00.000Z"
    }
  ],
  "nextCursor": null
}
```

## Getting URL Details

<CodeGroup>
  ```typescript TypeScript theme={null}
  const url = await zavu.urls.get("url_abc123");

  console.log(`URL: ${url.url}`);
  console.log(`Status: ${url.status}`);
  console.log(`Verified at: ${url.createdAt}`);
  ```

  ```python Python theme={null}
  url = zavu.urls.get("url_abc123")

  print(f"URL: {url.url}")
  print(f"Status: {url.status}")
  print(f"Verified at: {url.created_at}")
  ```

  ```ruby Ruby theme={null}
  url = client.urls.get("url_abc123")

  puts "URL: #{url.url}"
  puts "Status: #{url.status}"
  puts "Verified at: #{url.created_at}"
  ```

  ```go Go theme={null}
  url, _ := client.URLs.Get(context.TODO(), "url_abc123")

  fmt.Printf("URL: %s\n", url.URL)
  fmt.Printf("Status: %s\n", url.Status)
  fmt.Printf("Verified at: %s\n", url.CreatedAt)
  ```

  ```php PHP theme={null}
  $url = $client->urls->get('url_abc123');

  echo "URL: {$url->url}\n";
  echo "Status: {$url->status}\n";
  echo "Verified at: {$url->createdAt}\n";
  ```

  ```bash cURL theme={null}
  curl https://api.zavu.dev/v1/urls/{urlId} \
    -H "Authorization: Bearer $ZAVU_API_KEY"
  ```
</CodeGroup>

## Automatic Verification in Messages

When you send a message with URLs, Zavu automatically extracts and verifies them:

```typescript theme={null}
// URLs in message are automatically verified
const message = await zavu.messages.send({
  to: "+14155551234",
  text: "Check out our sale: https://mystore.example.com/sale",
});

// If URL is not pre-verified, it will be checked automatically
// If blocked, the message status will be "pending_url_verification"
```

### Message Status: `pending_url_verification`

If a message contains an unverified URL that requires manual review:

```json theme={null}
{
  "id": "msg_abc123",
  "to": "+14155551234",
  "status": "pending_url_verification",
  "text": "Check out: https://new-domain.example.com/page"
}
```

<Tip>
  Pre-verify URLs before sending campaigns to avoid delivery delays. Submit all your marketing URLs when setting up a new campaign.
</Tip>

## Best Practices

<CardGroup cols={2}>
  <Card title="Use Your Own Domain" icon="globe">
    Register and verify your own domains for consistent delivery
  </Card>

  <Card title="Avoid Shorteners" icon="link-slash">
    Always use full URLs instead of shortening services
  </Card>

  <Card title="Pre-Verify Campaign URLs" icon="check-double">
    Verify all URLs before launching broadcast campaigns
  </Card>

  <Card title="Use HTTPS" icon="lock">
    Always use HTTPS URLs for better security and trust
  </Card>
</CardGroup>

## Domain-Level Verification

Once a URL is verified, subsequent URLs from the same domain are typically verified faster:

```typescript theme={null}
// First URL from domain - full verification
await zavu.urls.submit({ url: "https://mystore.example.com/sale" });

// Additional URLs from same domain - faster verification
await zavu.urls.submit({ url: "https://mystore.example.com/products" });
await zavu.urls.submit({ url: "https://mystore.example.com/about" });
```

## Complete Example

```typescript theme={null}
import Zavudev from '@zavudev/sdk';

const zavu = new Zavudev();

async function sendCampaignWithLinks() {
  const campaignUrls = [
    "https://mystore.example.com/sale",
    "https://mystore.example.com/new-arrivals",
  ];

  // 1. Pre-verify all campaign URLs
  console.log("Verifying campaign URLs...");

  for (const url of campaignUrls) {
    try {
      const result = await zavu.urls.submit({ url });
      console.log(`${url}: ${result.url.status}`);

      if (result.url.status !== "approved") {
        throw new Error(`URL not approved: ${url}`);
      }
    } catch (error) {
      console.error(`Failed to verify ${url}:`, error);
      return;
    }
  }

  // 2. Send messages with verified URLs
  console.log("\nSending campaign...");

  const message = await zavu.messages.send({
    to: "+14155551234",
    text: `Sale alert! Shop now: ${campaignUrls[0]}`,
  });

  console.log(`Message sent: ${message.id} (${message.status})`);
}

sendCampaignWithLinks();
```

## Troubleshooting

### URL Rejected

If your URL is rejected:

1. Check that it's not a URL shortener
2. Ensure the URL is accessible (not 404)
3. Verify the domain has valid SSL
4. Contact support if you believe it's a false positive

### Message Stuck in `pending_url_verification`

1. Submit the URL manually using `POST /v1/urls`
2. Wait for verification (usually seconds)
3. The message will be released once approved

### Malicious URL Detection

If your legitimate URL is flagged as malicious:

1. Check [Google Safe Browsing](https://safebrowsing.google.com/) for your domain
2. Resolve any security issues on your site
3. Request a review from Google
4. Contact Zavu support with the resolution details
