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

# Quickstart

> Create a datasource and get an access token.

<Steps>
  <Step title="Create the connector">
    In **Admin → Connectors → Custom Source**, choose a datasource name such as `sales_orders` and document types `purchase_order,invoice`. Save the client ID and key fingerprint. If Pascal generates the private key, save it now: it is shown only once.
  </Step>

  <Step title="Install the client libraries">
    ```sh theme={null}
    pip install pyjwt[crypto] requests
    ```
  </Step>

  <Step title="Exchange a token">
    Replace the credential placeholders and private-key file path. This example uses Ed25519 (`EdDSA`); use `algorithm="ES256"` for P-256.

    ```python theme={null}
    from pathlib import Path
    import time
    import uuid

    import jwt
    import requests

    BASE_URL = "https://app.teampascal.com/api/v1/custom-source"
    TOKEN_URL = f"{BASE_URL}/token"
    CLIENT_ID = "<tenant_slug>/sales_orders"
    KID = "<key_fingerprint from Admin>"
    PRIVATE_KEY_PATH = "custom-source.key"


    def exchange_token():
        private_key_pem = Path(PRIVATE_KEY_PATH).read_text()
        iat = int(time.time())
        claims = {
            "iss": CLIENT_ID,
            "sub": CLIENT_ID,
            "aud": TOKEN_URL,
            "iat": iat,
            "exp": iat + 300,
            "jti": str(uuid.uuid4()),
        }
        assertion = jwt.encode(
            claims, private_key_pem, algorithm="EdDSA", headers={"kid": KID}
        )
        response = requests.post(
            TOKEN_URL,
            data={
                "grant_type": "client_credentials",
                "client_assertion_type": (
                    "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
                ),
                "client_assertion": assertion,
            },
            timeout=30,
        )
        response.raise_for_status()
        return response.json()["access_token"]


    TOKEN = exchange_token()
    ```

    For scheduled jobs, keep the key in your secret store and update `KID` after rotation.
  </Step>

  <Step title="Next: send records">
    Follow [Uploads](/uploads) for your integration, or [Test the Custom Source API](/test-the-api) for a dry run with sample records.
  </Step>
</Steps>
