# Quickstart

In five minutes: one decision with one call, then the same decision as a session where each side sends its own limits. You need `PREFSIFT_URL` and `PREFSIFT_API_KEY` (both come in the email with your key).

```sh
export PREFSIFT_URL="<the URL from your key email>"
export PREFSIFT_API_KEY="psk_..."
```

## 1. One call

A cleaner and a customer. The customer's agent passes on what its person said; the cleaner's rules are sent as data, so nothing needs reading and the call costs nothing.

```sh
curl -s -X POST "$PREFSIFT_URL/v1/decide" \
  -H "content-type: application/json" \
  -H "authorization: Bearer $PREFSIFT_API_KEY" \
  -d '{
  "request_id": "quickstart-1",
  "attributes": [
    {"attribute_id": "price", "type": "money", "currency": "USD"},
    {"attribute_id": "day", "type": "categorical", "domain": ["sat", "wed"]}
  ],
  "candidates": [
    {"candidate_id": "sat_240", "facts": {"price": "$240", "day": "sat"}},
    {"candidate_id": "sat_195", "facts": {"price": "$195", "day": "sat"}},
    {"candidate_id": "wed_180", "facts": {"price": "$180", "day": "wed"}}
  ],
  "parties": [
    {"party_id": "customer", "label": "The customer", "evidence": [
      {"evidence_id": "c1", "kind": "principal_quote", "said_by": "principal", "source": "SMS", "text": "Saturday please, and nothing over $200."}
    ]},
    {"party_id": "cleaner", "label": "Tidy Fern", "evidence": [
      {"evidence_id": "b1", "kind": "structured", "structured": {"rules": [
        {"rule_id": "floor", "kind": "constraint", "predicate": {"op": "ge", "attribute_id": "price", "value": "$180"}}
      ]}}
    ]}
  ]
}' | jq '.explanation.options'
```

You get one line per option:

```json
{
  "sat_240": "Crossed out: $240 is over the customer's $200 limit.",
  "wed_180": "Crossed out: the customer needs Saturday, not Wednesday.",
  "sat_195": "Kept: fits every limit, and no other option beats it."
}
```

*Crossed out* means an option breaks someone's limit; *beaten* means another option is as good for every side and better for one; *kept* means neither. `result.known_nondominated` is the list of kept options. If several are kept and you need one, add a tie-breaker: `"decision": {"selection_policy": {"kind": "declared_rules", "steps": [{"kind": "prefer", "attribute_id": "price", "direction": "lower"}]}}`.

## 2. A session, when each side should keep its limits private

The cleaner's agent starts the decision with its key and gets an invitation for the customer's agent. The customer's agent joins with its code (no API key) and sends its own side.

```sh
S=$(curl -s -X POST "$PREFSIFT_URL/v1/sessions" -H "content-type: application/json" -H "authorization: Bearer $PREFSIFT_API_KEY" -d '{
  "context": "Saturday clean: time and price",
  "attributes": [{"attribute_id": "price", "type": "money", "currency": "USD"}, {"attribute_id": "day", "type": "categorical", "domain": ["sat", "wed"]}],
  "candidates": [{"candidate_id": "sat_240", "facts": {"price": "$240", "day": "sat"}}, {"candidate_id": "sat_195", "facts": {"price": "$195", "day": "sat"}}, {"candidate_id": "wed_180", "facts": {"price": "$180", "day": "wed"}}],
  "initiator": {"participant_id": "cleaner", "label": "Tidy Fern", "party": {"party_id": "cleaner", "evidence": [{"evidence_id": "b1", "kind": "structured", "structured": {"rules": [{"rule_id": "floor", "kind": "constraint", "predicate": {"op": "ge", "attribute_id": "price", "value": "$180"}}]}}]}},
  "parties": [{"party_id": "customer", "label": "The customer", "redacted_as": "the customer"}]
}')
SID=$(echo "$S" | jq -r .session_id); CODE=$(echo "$S" | jq -r .invitations.customer.code)
echo "$S" | jq -r .invitations.customer.invitation    # send this to the customer's agent

curl -s -X POST "$PREFSIFT_URL/v1/sessions/$SID/join" -H "x-session-code: $CODE"
curl -s -X PUT "$PREFSIFT_URL/v1/sessions/$SID/parties/customer" -H "x-session-code: $CODE" -H "content-type: application/json" \
  -d '{"evidence": [{"evidence_id": "c1", "kind": "principal_quote", "said_by": "principal", "text": "Saturday please, and nothing over $200."}]}'
curl -s "$PREFSIFT_URL/v1/sessions/$SID/result" -H "x-session-code: $CODE" | jq '.explanation.options'
```

The customer sees its own limit named; the cleaner, reading with its own code (`.codes.cleaner`), sees "doesn't fit the customer" for `sat_240` and never the $200.

## Next

- Hold results near a limit for a person's sign-off: [Approvals](api.md#approvals-sign-off-before-a-result-goes-out).
- Get notified instead of polling: [Webhooks](api.md#webhooks).
- Every field and error: the [API reference](api.md). Exact meanings: [semantics](semantics.md).
