Sign with Privy

This guide shows how to use Privy embedded wallets to sign Due transfer intents via REST API.

Prerequisites

  • Privy App ID and Privy App Secret - found in your Privy application settings (App settings > Basics)
  • Due Platform API key - contact Due Network team for access
  • A Due account with a linked Privy wallet

For more information:

Signing Flow

When you create a crypto → fiat transfer in Due, you need to sign the transfer intent before funds can be moved. This involves:

  1. Creating a transfer intent
  2. Signing each signable object via Privy API
  3. Submitting the signed intent back to Due

Step 1: Create a Transfer Intent

First, create a transfer intent for your existing transfer:

curl --request POST \
  --url https://api.due.network/v1/transfers/{transfer_id}/transfer_intent \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/json' \
  --header 'Due-Account-Id: <ACCOUNT_ID>'

Response:

{
    "id": "ti_24QbulYAT9nfjU",
    "sender": "evm:0xcF5AaaBe14Ba42d9D765C8f2b9099c3b69a25321",
    "signables": [
        {
            "payload": {
                "kind": "typed_data",
                "value": {
                    "types": {
                        "Permit": [
                            {"name": "owner", "type": "address"},
                            {"name": "spender", "type": "address"},
                            {"name": "value", "type": "uint256"},
                            {"name": "nonce", "type": "uint256"},
                            {"name": "deadline", "type": "uint256"}
                        ]
                    },
                    "primaryType": "Permit",
                    "domain": {
                        "name": "USDC",
                        "version": "1",
                        "chainId": "2000",
                        "verifyingContract": "0xA52B297943dd6F3D5fFb41F50040BB2Bc6272F06"
                    },
                    "message": {
                        "owner": "0xcF5AaaBe14Ba42d9D765C8f2b9099c3b69a25321",
                        "spender": "0xC2E594095801A382894b761b511B44775e1716a6",
                        "value": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
                        "nonce": "0",
                        "deadline": "1759424402"
                    }
                }
            },
            "hash": "0x7ab1ccbb88a8cf030de759744c4ac249f06b2512242e5624058bcda99daf2576"
        },
        {
            "payload": {
                "kind": "typed_data",
                "value": {
                    "types": {
                        "PayoutIntent": [
                            {"name": "sender", "type": "address"},
                            {"name": "relay", "type": "address"},
                            {"name": "calls", "type": "bytes[]"},
                            {"name": "nonce", "type": "bytes32"},
                            {"name": "deadline", "type": "uint256"}
                        ]
                    },
                    "primaryType": "PayoutIntent",
                    "domain": {
                        "name": "DuePayout",
                        "version": "1",
                        "chainId": "2000",
                        "verifyingContract": "0xC2E594095801A382894b761b511B44775e1716a6"
                    },
                    "message": {
                        "sender": "0xcF5AaaBe14Ba42d9D765C8f2b9099c3b69a25321",
                        "relay": "0xBADDA95F65be56Dc4cD737E865a2d35F0B672BAD",
                        "calls": ["0xb9b8bc50..."],
                        "nonce": "0xae7813992f91fd41c18bed8734c8d1914d13adad6bfb80f656869813cbc170ad",
                        "deadline": "1759424402"
                    }
                }
            },
            "hash": "0x51dc7acb50075ea1ac934409c5d40e52b1744df19922894d35ba261c86777f95"
        }
    ],
    "expiresAt": "2025-10-02T17:00:02.25776447Z"
}

The response contains a signables array - typically two objects: Permit (token approval) and PayoutIntent (the actual transfer).

Important: Transfer intents expire (see expiresAt field). Complete signing before expiration.

Step 2: Sign Each Signable with Privy

For each object in the signables array, use Privy's eth_signTypedData_v4 method to generate a signature.

Example: Signing the Permit

curl --request POST \
  --url https://api.privy.io/v1/wallets/<wallet_id>/rpc \
  -u "<your-privy-app-id>:<your-privy-app-secret>" \
  --header 'privy-app-id: <your-app-id>' \
  --header 'Content-Type: application/json' \
  --data '{
    "method": "eth_signTypedData_v4",
    "params": {
      "typed_data": {
        "domain": {
          "name": "USDC",
          "version": "1",
          "chainId": "2000",
          "verifyingContract": "0xA52B297943dd6F3D5fFb41F50040BB2Bc6272F06"
        },
        "types": {
          "Permit": [
            {"name": "owner", "type": "address"},
            {"name": "spender", "type": "address"},
            {"name": "value", "type": "uint256"},
            {"name": "nonce", "type": "uint256"},
            {"name": "deadline", "type": "uint256"}
          ]
        },
        "primary_type": "Permit",
        "message": {
          "owner": "0xcF5AaaBe14Ba42d9D765C8f2b9099c3b69a25321",
          "spender": "0xC2E594095801A382894b761b511B44775e1716a6",
          "value": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
          "nonce": "0",
          "deadline": "1759424402"
        }
      }
    }
  }'

Response:

{
  "method": "eth_signTypedData_v4",
  "data": {
    "signature": "0xd99802ab7a14b535ad0bf9c69a7cfd862797a5f3b48270db20fdbd7a170a433e79970506944a3289e1ee2c7d0324b6097d9f67c51816792dbbac0f8c2c8af4b11b",
    "encoding": "hex"
  }
}

Repeat this process for the PayoutIntent signable, using its respective domain, types, and message values from the transfer intent response.

For more details, see Privy: Sign Typed Data.

Step 3: Submit the Signed Intent

After obtaining signatures for all signables, submit the complete transfer intent with signatures to Due:

curl --request POST \
  --url https://api.due.network/v1/transfer_intents/submit \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  --header 'Content-Type: application/json' \
  --header 'Due-Account-Id: <ACCOUNT_ID>' \
  --data '{
    "id": "ti_24QbulYAT9nfjU",
    "ownerId": "acct_DkRHlTg5q8VZM6Gn",
    "sender": "evm:0xcF5AaaBe14Ba42d9D765C8f2b9099c3b69a25321",
    "amountIn": "1177.29598",
    "to": {
      "evm:0xbad09Fb9781D9D57E1423231AC51f9bb9e0CABAD": "1177.29598"
    },
    "tokenIn": "USDC",
    "tokenOut": "USDC",
    "networkIdIn": "base",
    "networkIdOut": "base",
    "signables": [
      {
        "payload": {
          "kind": "typed_data",
          "value": { ... }
        },
        "hash": "0x7ab1ccbb88a8cf030de759744c4ac249f06b2512242e5624058bcda99daf2576",
        "signature": "0xd99802ab7a14b535ad0bf9c69a7cfd862797a5f3b48270db20fdbd7a170a433e..."
      },
      {
        "payload": {
          "kind": "typed_data",
          "value": { ... }
        },
        "hash": "0x51dc7acb50075ea1ac934409c5d40e52b1744df19922894d35ba261c86777f95",
        "signature": "0xa1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456..."
      }
    ],
    "nonce": "0xae7813992f91fd41c18bed8734c8d1914d13adad6bfb80f656869813cbc170ad",
    "hash": "0x51dc7acb50075ea1ac934409c5d40e52b1744df19922894d35ba261c86777f95",
    "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
    "expiresAt": "2025-10-02T17:00:02.25776447Z",
    "createdAt": "2025-10-02T16:45:02.302661636Z",
    "reference": "tf_2ghR6HgkRHTQB8:deposit"
  }'

Response:

{
  "id": "ti_24QbulYAT9nfjU",
  "status": "payment_submitted",
  "txHashIn": "0x1234567890abcdef...",
  "createdAt": "2025-10-02T16:45:02Z"
}

The transfer is now submitted and will be processed by Due.

Alternative: Funding Address

For a simpler approach that doesn't require signing, you can use a funding address instead. See Due: Stablecoin to Fiat Transfers for details.

Additional Resources