> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shieldlabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Angular

> Identify a visitor, send a Request ID with your application action and verify the result on your server.

Identify a visitor, send a Request ID with your application action and verify the result on your server. This guide uses the supported ShieldLabs package API, not a standalone generated client.

## Before you start

An existing Angular 17+ application and a Public Key. The complete example below uses Angular 22; Angular 17/18 components require standalone: true.

Register and verify the website domain in your [analytics dashboard](https://app.shieldlabs.ai/). Use credentials from the same domain and environment as the browser check. For a fresh account, start with [Quick Start](/quickstart).

## Get your credentials

Copy the Public Key for your domain from **Integration > API keys**. A public frontend environment variable may contain this key, but never a Private API Key or webhook signing secret.

## Install the SDK

```bash theme={null}
npm install @shieldlabs-ai/angular @shieldlabs-ai/js
```

Use `yarn add` or `pnpm add` with the same package names if your project uses that package manager. Framework packages remain the application's responsibility.

## Add the integration

The snippets are complete files of a new Angular 22 application (`ng new`). In an existing
application, add the ShieldLabs lines to your own files; before Angular 20 the root component is
`AppComponent` in `app.component.ts`, and in Angular 17 and 18 components also need
`standalone: true`.

Add the provider next to the providers that are already there:

```ts theme={null}
// src/app/app.config.ts
import { provideHttpClient } from '@angular/common/http';
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideShieldLabs } from '@shieldlabs-ai/angular';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideRouter(routes),
    provideHttpClient(), // skip it if your app already provides HttpClient
    provideShieldLabs({ publicKey: '0123456789abcdef0123456789abcdef' }),
  ],
};
```

Run an identification when the form is submitted and send the request ID with it:

```ts theme={null}
// src/app/signup-form.ts
import { HttpClient } from '@angular/common/http';
import { Component, inject } from '@angular/core';
import { injectIdentify } from '@shieldlabs-ai/angular';
import { firstValueFrom } from 'rxjs';

@Component({
  selector: 'app-signup-form',
  template: `
    <form (submit)="submit($event)">
      <input name="email" type="email" required />
      <button [disabled]="identification.isLoading()">Sign up</button>
    </form>
  `,
})
export class SignupForm {
  private readonly http = inject(HttpClient);
  protected readonly identification = injectIdentify();

  protected async submit(event: SubmitEvent): Promise<void> {
    event.preventDefault();
    const email = new FormData(event.target as HTMLFormElement).get('email');
    // null when there is no identification: send the signup anyway, your server treats it as unverified.
    const result = await this.identification.identify();
    await firstValueFrom(this.http.post('/api/signup', { email, requestId: result?.requestId ?? null }));
  }
}
```

Render the form in your root component (the generated `app.html` and `app.css` are then unused):

```ts theme={null}
// src/app/app.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

import { SignupForm } from './signup-form';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, SignupForm],
  template: `
    <app-signup-form />
    <router-outlet />
  `,
})
export class App {}
```

`/api/signup` is your own endpoint. On your server, read the verdict for `requestId`, for example
with [`@shieldlabs-ai/node`](https://github.com/ShieldLabs-ai/shieldlabs-node):

```ts theme={null}
import { ShieldLabs } from '@shieldlabs-ai/node';

const shieldlabs = new ShieldLabs({ apiKey: process.env.SHIELDLABS_API_KEY! });

// Call it with the requestId from the body of POST /api/signup.
export async function readVerdict(requestId: string | null) {
  if (!requestId) return null; // no identification: unverified, never clean
  // Polls the History API (with backoff) until the row of this identification is there.
  return shieldlabs.identifications.get(requestId);
}
```

The History row appears about 1-3 seconds after `identify()` resolves and can be refined for up to
about 10 seconds as follow-up checks finish. To keep that wait off the submit, start the
identification when the user begins the action (see
[Start the identification early ](https://github.com/ShieldLabs-ai/shieldlabs-angular/blob/03cffc569c311a604d65a047e975b55c6c1fb460/README.md#start-the-identification-early)).

> **Test on a registered domain.** ShieldLabs records identifications only for the domains
> registered in your account. On `localhost` (`ng serve`) the page still receives a `requestId`,
> but no identification is recorded and your backend never finds it. To try the whole path, open
> the page from a registered development domain with its own keys.

A complete application is in [`examples/standalone`](https://github.com/ShieldLabs-ai/shieldlabs-angular/tree/03cffc569c311a604d65a047e975b55c6c1fb460/examples/standalone), and
[Unit tests of your components ](https://github.com/ShieldLabs-ai/shieldlabs-angular/blob/03cffc569c311a604d65a047e975b55c6c1fb460/README.md#unit-tests-of-your-components) shows the test providers the
generated `app.spec.ts` needs after these steps.

## Test the complete flow

1. Run the application on the registered HTTPS domain with its Public Key.
2. Trigger the protected form once and check that a Request ID is sent to your own backend.
3. Retrieve that same ID using a server SDK or locate it in the analytics dashboard.
4. Test a missing ID and an agent load failure: your backend must treat the action as unverified.

The `/api/signup` routes shown in examples belong to your application; they are not ShieldLabs API endpoints. Connect one of the [server quick starts](/api/sdks#server-packages) before testing the full action. A successful browser call does not prove scoring is complete.

## Troubleshooting

* No History row: confirm the registered domain, credential/environment match and that the browser remained open while collectors posted.
* Missing or pending verdict: scoring is asynchronous. The server helper waits within a bounded budget; handle a missing result and API errors explicitly.
* Authentication error: use the Private API Key for History, not a Public Key, Management Secret Key or MCP OAuth token.
* Invalid webhook signature: verify the original raw bytes with the endpoint's full signing secret, before trusting parsed JSON.

## Next steps

* [Runnable example](https://github.com/ShieldLabs-ai/shieldlabs-angular/tree/03cffc569c311a604d65a047e975b55c6c1fb460/examples/standalone)
* [SDK reference and changelog](https://github.com/ShieldLabs-ai/shieldlabs-angular)
* [Identification flow](/api/identification-flow)
* [Server API](/api/server-api)
* [Webhook setup](/setup/webhooks)
* [Content Security Policy](/setup/csp)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.