Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Platform — Getting Started

Set up Portal for authentication, payments, profiles, and more. This guide gets you running in minutes.

Just need age verification? See the Age Verification Quick Start instead — it's simpler.

1. Get a Portal instance

Sign up at hub.getportal.cc and create a Portal instance. PortalHub hosts and runs it for you — no servers needed.

You'll get:

  • An instance URL (e.g. https://your-instance.hub.getportal.cc)
  • An API auth token

Option B: Self-host with Docker

If you prefer to run your own instance:

docker run -d -p 3000:3000 \
  -e PORTAL__AUTH__AUTH_TOKEN=my-secret-token \
  -e PORTAL__NOSTR__PRIVATE_KEY=$(openssl rand -hex 32) \
  getportal/sdk-daemon:0.4.1

Check it's running:

curl http://localhost:3000/health
# → OK

See Docker Deployment for production setup.

3. Install an SDK (optional)

Portal exposes a standard HTTP REST API — you can use any language. SDKs add convenience.

HTTP

Nothing to install. Set your base URL and token:

export BASE_URL=http://localhost:3000
export AUTH_TOKEN=my-secret-token
JavaScript
npm install portal-sdk

Node.js 18+ required.

Java

Gradle:

repositories {
    maven { url 'https://jitpack.io' }
}
dependencies {
    implementation 'com.github.PortalTechnologiesInc:java-sdk:0.4.1'
}

Maven:

<repository>
    <id>jitpack.io</id>
    <url>https://jitpack.io</url>
</repository>
<dependency>
    <groupId>com.github.PortalTechnologiesInc</groupId>
    <artifactId>java-sdk</artifactId>
    <version>0.4.1</version>
</dependency>

4. First request — authenticate a user

Generate a URL for a user to log in:

HTTP
# Start the handshake — get a URL to show the user
curl -s -X POST $BASE_URL/key-handshake \
  -H "Authorization: Bearer $AUTH_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
# → { "stream_id": "abc123", "url": "nostr+walletconnect://..." }

# Show the URL to the user (QR code, link, etc.)
# Then poll for the user's public key:
curl -s "$BASE_URL/events/abc123?after=0" \
  -H "Authorization: Bearer $AUTH_TOKEN"
# → { "events": [{ "index": 0, "data": { "main_key": "USER_PUBKEY_HEX" } }] }

See REST API for the full async polling pattern.

JavaScript
import { PortalClient } from 'portal-sdk';

const client = new PortalClient({
  baseUrl: 'http://localhost:3000',
  authToken: 'my-secret-token'
});

const { url, stream } = await client.newKeyHandshakeUrl();
console.log('Share with user:', url);

// Wait for the user to scan/click the URL
const result = await client.poll(stream);
console.log('User key:', result.main_key);
Java
import cc.getportal.PortalClient;
import cc.getportal.PortalClientConfig;

PortalClient client = new PortalClient(
    PortalClientConfig.create("http://localhost:3000", "my-secret-token")
);

var operation = client.newKeyHandshakeUrl();
System.out.println("Share with user: " + operation.url());

var result = client.pollUntilComplete(operation);
System.out.println("User key: " + result.main_key());

What's next?

Common issues

IssueFix
Connection refusedPortal not running or wrong port. Check docker ps.
401 UnauthorizedToken must match PORTAL__AUTH__AUTH_TOKEN.
Invalid Nostr keyUse hex (64 chars); convert nsec with nak decode nsec1....

Troubleshooting: Full troubleshooting guide