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

# Callback URLs

> How OAuth callback URLs work, the exact-match rule, and how to register yours.

A **callback URL** (also called *redirect URI*) is the URL Daysync redirects the user's browser to after they authorize your integration. It's where your backend receives the one-time authorization `code`.

## Why exact-match matters

The portal validates the `redirect_uri` parameter on every `/authorize` request against the allowlist registered for **your app**. Anything not on the allowlist is rejected — the authorize page stops with *"This application's redirect URL is not registered"* **before any redirect happens**, so the code is never sent anywhere.

This is a security feature: if the allowlist weren't enforced, an attacker could craft a phishing URL that sends the authorization code to their own server. Only a URL you control should be on your list.

<Warning>
  **Exact match means character-for-character.** All of these are treated as different URLs:

  * `https://acme.com/cb` vs `https://acme.com/cb/` (trailing slash)
  * `https://acme.com/cb` vs `https://www.acme.com/cb` (subdomain)
  * `https://acme.com/cb` vs `http://acme.com/cb` (scheme)
  * `https://acme.com/cb` vs `https://acme.com/cb?x=1` (query string)
  * `https://acme.com:443/cb` vs `https://acme.com/cb` (explicit port)
</Warning>

## How to register yours — self-service, instant

You manage your own redirect URIs on your app page in the [Integrations Portal](https://integrations.daysync.com). There's nothing to email and no Daysync review — a saved URL is enforced immediately.

<Steps>
  <Step title="Open your app in the portal">
    Go to the [Integrations Portal](https://integrations.daysync.com), open your application, and find the **Redirect URIs** section.
  </Step>

  <Step title="Add the URLs you need">
    Typically one URL per environment:

    * `https://yourapp.com/oauth/daysync/callback` (production)
    * `https://staging.yourapp.com/oauth/daysync/callback` (staging)
    * `http://localhost:3000/oauth/daysync/callback` (local development)

    Use distinct paths if you handle Daysync separately from other OAuth providers in your app.
  </Step>

  <Step title="Save — it's live immediately">
    The allowlist takes effect right away; there's no sync delay. Your `/authorize` request must then include the URL verbatim:

    ```text theme={null}
    https://integrations.daysync.com/authorize
      ?response_type=code
      &client_id=YOUR_API_KEY
      &redirect_uri=https://yourapp.com/oauth/daysync/callback
      &scope=…
      &code_challenge=…
      &code_challenge_method=S256
    ```

    The same URL must appear in your subsequent `/api/oauth/token` call.
  </Step>
</Steps>

## URL rules

| Rule                                         | Why                                                                                       |
| -------------------------------------------- | ----------------------------------------------------------------------------------------- |
| **Use HTTPS** in production                  | Auth codes transmitted in cleartext over HTTP would be vulnerable to network interception |
| **Localhost may be HTTP**                    | Development convenience: `http://localhost:<port>` is fine for local testing              |
| **No fragments** (`#...`)                    | The browser strips fragments before redirect, so they'd never match                       |
| **Must include the path** if the URL has one | `https://acme.com` and `https://acme.com/` are different from `https://acme.com/callback` |

## Common mistakes

<AccordionGroup>
  <Accordion title="'This application's redirect URL is not registered'">
    Your `/authorize` `redirect_uri` doesn't match your app's allowlist character-for-character. Most common causes:

    * Trailing slash difference
    * HTTP vs HTTPS
    * URL-encoded (`https%3A%2F%2F…`) when it should be plain
    * Subdomain mismatch (`acme.com` registered, `www.acme.com` sent)

    Print the literal `redirect_uri` value you're sending and compare it against the entry saved on your app page — then add or correct it in the portal.
  </Accordion>

  <Accordion title="redirect_uri mismatch at the token step">
    The `redirect_uri` in your `/api/oauth/token` request must be **identical** to the one you used in the `/authorize` request that produced the code. A mismatch returns `400 invalid_grant`.
  </Accordion>

  <Accordion title="Wildcard or pattern matching">
    Wildcards are not supported. If you have many environments, register each URL explicitly. `https://*.yourapp.com/callback` will not match anything.
  </Accordion>
</AccordionGroup>

## Production hygiene

<Note>
  Once your production callback is registered, **remove your localhost and Postman callbacks** from your app's Redirect URIs. Each entry is a potential phishing target — fewer entries means a smaller attack surface. Because registration is self-service, you can prune them yourself at any time.
</Note>
