Consent Docs

Record consent from your server

Record a person's answers with a secret key when consent is given outside the banner, then change or withdraw the record.

How-to3 min readFor web, backend and mobile developers

The banner records what a visitor answers in the browser. Use your server when consent is given somewhere else: a sign-up form, a screen in your own app, a call-centre script or an import.

Before you start#

  • A published form that offers the purposes you ask about, and its key. The form does not have to be shown as a banner.
  • A secret key. Every secret key can record, read, change and withdraw consent.
  • The Node.js SDK or the Python SDK installed, or any HTTP client.

Record the answers#

  1. On your server, create a client with the secret key from the environment.

    Node.jsJavaScript
    import { ConsentClient } from '@piedap/consent/server';
    
    const client = new ConsentClient({
      apiUrl: 'https://console.dpconsent.com/consent',
      secretKey: process.env.PIEDAP_SECRET_KEY,
    });
    Python
    import os
    
    from piedap_consent import ConsentClient, Decision
    
    client = ConsentClient(
        api_url="https://console.dpconsent.com/consent",
        secret_key=os.environ["PIEDAP_SECRET_KEY"],
    )
    cURLShell
    export PIEDAP_SECRET_KEY='sk_…'
  2. Send one decision for every purpose on the form that is not always on. A box left unticked is an answer too.

    Node.jsJavaScript
    const record = await client.recordConsent({
      formKey: 'kalpa-account',
      subjectId: 'usr_8f2K1xQz',
      subjectType: 'USER_ID',
      countryCode: 'IN',
      interaction: 'SAVE_PREFERENCES',
      decisions: [
        { purposeKey: 'course-updates-by-email', granted: true },
        { purposeKey: 'class-reminders-by-sms', granted: false },
      ],
      idempotencyKey: 'signup-usr_8f2K1xQz',
    });
    
    console.log(record.consentId, record.status);
    Python
    consent = client.record_consent(
        form_key="kalpa-account",
        subject_id="usr_8f2K1xQz",
        subject_type="USER_ID",
        country_code="IN",
        interaction="SAVE_PREFERENCES",
        decisions=[
            Decision("course-updates-by-email", True),
            Decision("class-reminders-by-sms", False),
        ],
        idempotency_key="signup-usr_8f2K1xQz",
    )
    
    print(consent.consent_id, consent.status)
    cURLShell
    curl -X POST https://console.dpconsent.com/consent/sdk/v1/consent \
      -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: signup-usr_8f2K1xQz" \
      -d '{
        "formKey": "kalpa-account",
        "subjectId": "usr_8f2K1xQz",
        "subjectType": "USER_ID",
        "countryCode": "IN",
        "interaction": "SAVE_PREFERENCES",
        "decisions": [
          { "purposeKey": "course-updates-by-email", "granted": true },
          { "purposeKey": "class-reminders-by-sms", "granted": false }
        ]
      }'
  3. Save the returned consentId next to the user. It is the simplest way to match a later webhook to a person.

The call returns the record. Its status is GRANTED when every optional purpose is allowed, PARTIAL when some are and DENIED when none is. A record written with a test key carries "test": true and a Test badge under Records in the console.

There is one record per person per form. A second call for the same identifier and form updates that record and returns the same consentId.

Get the fields right#

Field Why it matters
subjectType Defaults to COOKIE_ID. Send EMAIL, PHONE, USER_ID or another type, the same one every time. See Identifiers.
countryCode Decides which law is stamped on the record. Without it the API locates your server, not the person.
interaction Says how the choice was made. From a server it defaults to API.
idempotencyKey Names the event, so a retry returns the first response and records nothing twice.

A missing answer is refused with 400 INVALID_PURPOSE_DECISIONS, and missingPurposeKeys lists what to add. To read a form's purposes first, call resolveForm. Consent endpoints lists every field, value and error.

Change or withdraw one record#

To change answers, send only the purposes that changed. The others keep their answer.

Node.jsJavaScript
await client.updateConsent(
  consentId,
  [{ purposeKey: 'class-reminders-by-sms', granted: true }],
  { interaction: 'PREFERENCE_CENTRE' },
);

await client.withdrawConsent(consentId, 'Asked by phone');
Python
client.update_consent(
    consent_id,
    [Decision("class-reminders-by-sms", True)],
    interaction="PREFERENCE_CENTRE",
)

client.withdraw_consent(consent_id, reason="Asked by phone")
cURLShell
curl -X PATCH https://console.dpconsent.com/consent/sdk/v1/consent/$CONSENT_ID \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "decisions": [
      { "purposeKey": "class-reminders-by-sms", "granted": true }
    ],
    "interaction": "PREFERENCE_CENTRE"
  }'

curl -X POST https://console.dpconsent.com/consent/sdk/v1/consent/$CONSENT_ID/withdraw \
  -H "Authorization: Bearer $PIEDAP_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "Asked by phone" }'

A withdrawal refuses every optional purpose and sets the status to WITHDRAWN. Strictly necessary purposes stay on. Before you act on a purpose, check both its decision and the record's status: a WITHDRAWN, EXPIRED or PENDING record allows nothing optional.

Less common cases#

Link the person's other identifiers

Send identifiers, up to five, so the record can be found, withdrawn and erased by any of them. See linked identifiers.

Bring in consent given earlier

Send collectedAt with the time the consent was given. Only a secret key may send it. A record that is newer, withdrawn or erased since is left as it is. See Bring in consent given earlier.

Consent taken by your staff or on paper

Send capture with the channel, how the person confirmed and your reference to the evidence. See Record consent taken in a branch, by phone or on paper.

Consent for a child or through a guardian

Send dataPrincipalType and guardian. They are accepted only from a secret key, after your server has verified the parent or guardian. Consent endpoints lists the values and the rules.

If your own sign-up asks the person's age, send their answer as ageDeclaration: { eighteenOrOver: false }. The record is kept as a child's, and a grant then needs the verified guardian as well.

Keep an inactive person from being erased

If your team turned on Erase after inactivity, call recordActivity when the person signs in. See Activity.

↑↓ to move, Enter to open