Getting started
This guide takes a program from nothing to a first API call. The program signs in as a machine
client: a service identity that calls the API for one organization, with a key pair instead of a
password. Examples use curl and openssl.
Applications that act for users on their own are not supported yet. The API accepts user tokens only from Fedwright’s own clients. A program that runs by itself, on behalf of one organization, uses a machine client.
This environment
Section titled “This environment”| Setting | Value |
|---|---|
| API base URL | https://api-staging.fedwright.com/v1 |
| Interactive docs | https://api-staging.fedwright.com/docs/ |
| Issuer (sign-in) | https://auth-staging.fedwright.com |
| Environment’s scope | openid urn:zitadel:iam:org:project:id:394731229330538643:aud |
Every path in these guides is relative to the API base URL. The scope names the API as the audience of your token. Its project ID is the same for every client in this environment, so use the scope as it is shown.
Before you start
Section titled “Before you start”- An organization, and a person with the owner role in it. Only an owner creates machine clients.
curl,openssl, andjq.
1. Generate a key pair
Section titled “1. Generate a key pair”You keep the private key. Fedwright stores only the public key. The key must be RSA, 2048 bits or longer.
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out client-private.pemopenssl pkey -in client-private.pem -pubout -out client-public.pemKeep client-private.pem out of source control and logs.
2. Create the machine client
Section titled “2. Create the machine client”An organization owner creates the machine client with createMachineClient. The request needs:
- The owner’s user token. A machine client can’t call this operation. It answers
403with the Problem typeclient-not-allowed. - Proof of a second factor with the owner’s sign-in. Without it, the answer is
403withmfa-required. - An
Idempotency-Keyheader. See Idempotency.
Choose the scopes the client may use from opportunities:read, org:read, searches:write,
matches:read, and ai:use. Authentication says what each allows.
The example grants three: opportunities:read for the first call below, org:read for the
conditional request samples, and searches:write for the saved-search
samples there and in Idempotency. Grant only what your program needs.
export API_BASE='https://api-staging.fedwright.com/v1'export ORG_ID='YOUR_ORGANIZATION_ID'export OWNER_TOKEN='OWNER_USER_ACCESS_TOKEN'
curl -sS "$API_BASE/orgs/$ORG_ID/machine-clients" \ -H "Authorization: Bearer $OWNER_TOKEN" \ -H "Idempotency-Key: $(uuidgen)" \ -H 'Content-Type: application/json' \ -d "$(jq -n --arg pem "$(cat client-public.pem)" \ '{name: "Nightly import", scopes: ["opportunities:read", "org:read", "searches:write"], publicKeyPem: $pem}')"The answer is 201 with the machine client. Keep two values from it:
subject: the identity your token requests name asissandsub. It is not the client’sid.keys[0].keyId: thekidyou put in the header of each signed assertion.
The id is the client’s identifier in the API’s own paths, such as
/orgs/{orgId}/machine-clients/{id}.
3. Get an access token
Section titled “3. Get an access token”The client proves its identity by signing a short JWT, the assertion, with its private key, and
exchanging it for an access token. Authentication explains each field. The
script below builds the assertion with openssl and asks for a token.
export ISSUER='https://auth-staging.fedwright.com'export SUBJECT='SUBJECT_FROM_THE_CREATE_RESPONSE'export KEY_ID='KEY_ID_FROM_THE_CREATE_RESPONSE'export SCOPE='openid urn:zitadel:iam:org:project:id:394731229330538643:aud urn:zitadel:iam:user:metadata'
enc() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }
header=$(jq -nc --arg kid "$KEY_ID" '{alg: "RS256", typ: "JWT", kid: $kid}' | enc)now=$(date +%s)claims=$(jq -nc --arg sub "$SUBJECT" --arg aud "$ISSUER" --arg jti "$(openssl rand -hex 16)" \ --argjson now "$now" \ '{iss: $sub, sub: $sub, aud: $aud, iat: $now, exp: ($now + 300), jti: $jti}' | enc)signature=$(printf '%s' "$header.$claims" \ | openssl dgst -sha256 -sign client-private.pem -binary | enc)
token=$(curl -fsS "$ISSUER/oauth/v2/token" \ -d 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer' \ --data-urlencode "scope=$SCOPE" \ --data-urlencode "assertion=$header.$claims.$signature" | jq -r .access_token)The token request asks for the API’s audience scope and for urn:zitadel:iam:user:metadata. Without
the second scope the token still works, but it carries no organization or plan claim, so
rate limits can’t be applied to your organization’s plan.
4. Make a first call
Section titled “4. Make a first call”Send the token as a bearer token. This call lists two notices and needs opportunities:read.
curl -sS "https://api-staging.fedwright.com/v1/notices?limit=2" \ -H "Authorization: Bearer $token"The answer is a page of results, { "items": [...], "nextCursor": "..." }. Follow nextCursor for
the next page. See Pagination.
When a call fails, the body says why. See Errors.
What to read next
Section titled “What to read next”- Authentication: the assertion, scopes, key rotation, and token lifetime.
- Errors: the Problem Details format and every problem type.
- Rate limits: quotas, the headers that report them, and what to do on a
429. - Pagination, Idempotency, and Conditional requests.
- The API reference lists every operation. Its pages link to the interactive docs at
https://api-staging.fedwright.com/docs/.