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
Option A: PortalHub (recommended)
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.
Nothing to install. Set your base URL and token:
export BASE_URL=http://localhost:3000
export AUTH_TOKEN=my-secret-token
npm install portal-sdk
Node.js 18+ required.
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:
# 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.
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);
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?
- Authentication — Full auth flow with subkeys and static tokens
- Single Payments — Accept one-time payments
- Recurring Payments — Set up subscriptions
- Docker Deployment — Production deployment
- Environment Variables — All configuration options
Common issues
| Issue | Fix |
|---|---|
| Connection refused | Portal not running or wrong port. Check docker ps. |
| 401 Unauthorized | Token must match PORTAL__AUTH__AUTH_TOKEN. |
| Invalid Nostr key | Use hex (64 chars); convert nsec with nak decode nsec1.... |
Troubleshooting: Full troubleshooting guide