---
title: "Registration and newsletters for publishers | Snowplow Signals"
description: "Snowplow Signals tells your site how deep a reader is into a section and how often they come back, so it chooses when to ask and which newsletter to offer."
source: https://signals.snowplow.io/solutions/media/registration
---

[Media and publishing](https://signals.snowplow.io/solutions/media) / Registration and newsletters 

# A sign-up prompt that knows when to ask. 

Signals tells your site how deep a reader is into a section this visit and how often they have come back, so the registration wall and the newsletter prompt can wait for the moment a reader would say yes.

[Start free](https://signals.snowplow.io/start) [Talk to us](https://signals.snowplow.io/start?intent=talk) 

Higher registration and newsletter sign-up

## Get more readers to sign up by asking the ones who are ready. 

Registration and newsletter sign-up move when the prompt can tell a first visit from a fourth, and a passing reader from one who is three stories into politics. Signals gives your site how many stories a reader has opened in a section this visit, how often they have come back and how many times they have closed the prompt, so it can hold the ask, make it, or change what it asks for.

Four ways publishers use it

01 Readers deep in one section Third politics story this visit. Offer the politics newsletter. 02 First-time readers from search First visit, in from search, one story read. Hold the wall. 03 Returning readers without an account Third visit this week, just finished a story. Ask now. 04 Readers who keep closing the prompt Closed it twice this week, no sign-up. Stop asking the same way. 

[Read the docs →](https://docs.snowplow.io/docs/signals/introduction/) 

### Offer the section newsletter on the third story in it

A reader opened a politics story, then a second, and is now on their third this visit. A site-wide prompt that fires on a page count shows them the general newsletter here, and the general newsletter is not what they came for. Three stories in, they have told you what they read. The ask that fits is the politics newsletter, on this page, before they leave.

What Signals knows

`top_section_session` 

The section they have read most this visit

`stories_by_section_session` 

How many stories they opened in each section this visit

What your site does

Shows the newsletter for the section a reader has read most once they open a third story in it this visit, and names the section in the prompt.

Define it · PythonUse it · TypeScript

article.tsx · 16 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import Attribute, EntityProperty, Event, StreamAttributeGroup, domain_sessionid

page_view = Event(name="page_view", vendor="com.snowplowanalytics.snowplow", version="1-0-0")
section = EntityProperty(vendor="com.example", name="article", major_version=1, path="section")

top_section_session = Attribute(
    name="top_section_session",
    type="string",
    events=[page_view],
    aggregation="most_frequent",
    property=section,
    ttl=timedelta(hours=2),
)

stories_by_section_session = Attribute(
    name="stories_by_section_session",
    type="dict",
    events=[page_view],
    aggregation="category_count",
    property=section,
    ttl=timedelta(hours=2),
)

signup_section = StreamAttributeGroup(
    name="signup_section",
    version=1,
    attribute_key=domain_sessionid,
    owner="you@example.com",
    attributes=[top_section_session, stories_by_section_session],
)
```

```
// In the code that renders the article page
const reader = await signals.getServiceAttributes({
  name: "signup_section",
  attribute_key: "domain_sessionid",
  identifier: sessionId,
});
// { top_section_session: "politics", stories_by_section_session: { politics: 3, business: 1 } }

const inSection = reader.stories_by_section_session[reader.top_section_session] ?? 0;

if (inSection >= 3) {
  newsletterPrompt.show({
    list: reader.top_section_session,
    headline: `Get our ${reader.top_section_session} coverage by email`,
  });
}
```

### Hold the registration wall on a first visit

A reader found a story through a search result and has read it. The registration wall is set to drop on the second page, so it meets them on the next story, when one article is the only reason they have to hand over an email. A reader who leaves at that point was asked too early, and the wall has spent its one chance on the wrong visit. Let them read this time. The wall waits for the second visit, when coming back is the reason to register.

What Signals knows

`visits_7d` 

Separate visits in the last seven days

`arrived_from` 

The referrer medium on their first event

What your site does

Holds the registration wall through a first visit that came from search, and shows it on the next visit, when the reader has come back for more.

Define it · PythonUse it · TypeScript

article.tsx · 15 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import Attribute, AtomicProperty, Event, StreamAttributeGroup, domain_userid

page_view = Event(name="page_view", vendor="com.snowplowanalytics.snowplow", version="1-0-0")

visits_7d = Attribute(
    name="visits_7d",
    type="int32",
    events=[page_view],
    aggregation="approx_count_distinct",
    property=AtomicProperty(name="domain_sessionid"),
    period=timedelta(days=7),
)

arrived_from = Attribute(
    name="arrived_from",
    type="string",
    events=[page_view],
    aggregation="first",
    property=AtomicProperty(name="refr_medium"),
)

signup_first_visit = StreamAttributeGroup(
    name="signup_first_visit",
    version=1,
    attribute_key=domain_userid,
    owner="you@example.com",
    attributes=[visits_7d, arrived_from],
)
```

```
// In the code that renders the article page
const reader = await signals.getServiceAttributes({
  name: "signup_first_visit",
  attribute_key: "domain_userid",
  identifier: domainUserId,
});
// { visits_7d: 1, arrived_from: "search" }

if (reader.visits_7d === 1 && reader.arrived_from === "search") {
  registrationWall.hold();
} else if (reader.visits_7d >= 2) {
  registrationWall.show({
    headline: "Welcome back. Register to keep reading",
  });
}
```

### Ask for registration at the end of a story on a return visit

This reader is on their third visit this week and has no account. They have just read a story to the end. A wall that drops on page two met them on their first visit, when they had no reason to register, and has shown them the same thing since. A prompt timed to the end of a completed read on a return visit asks the reader with the most reason to say yes, at the moment they have it.

What Signals knows

`visits_7d` 

Separate visits in the last seven days

`completed_reads_today` 

Stories read to the end today

What your site does

Asks a reader with no account to register on their third visit in a week, once they have just finished a story, and puts the ask at the end of the story rather than over the top of it.

Define it · PythonUse it · TypeScript

article.tsx · 14 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import (
    Attribute, AtomicProperty, Criteria, Criterion, Event, EventProperty, StreamAttributeGroup, domain_userid,
)

page_view = Event(name="page_view", vendor="com.snowplowanalytics.snowplow", version="1-0-0")
article_read = Event(name="article_read", vendor="com.example", version="1-0-0")

visits_7d = Attribute(
    name="visits_7d",
    type="int32",
    events=[page_view],
    aggregation="approx_count_distinct",
    property=AtomicProperty(name="domain_sessionid"),
    period=timedelta(days=7),
)

completed_reads_today = Attribute(
    name="completed_reads_today",
    type="int32",
    events=[article_read],
    aggregation="counter",
    criteria=Criteria(all=[
        Criterion.gte(EventProperty(vendor="com.example", name="article_read", major_version=1, path="percent_read"), 90),
    ]),
    period=timedelta(days=1),
    default_value=0,
)

signup_returning = StreamAttributeGroup(
    name="signup_returning",
    version=1,
    attribute_key=domain_userid,
    owner="you@example.com",
    attributes=[visits_7d, completed_reads_today],
)
```

```
// In the code that runs when a reader reaches the end of a story
const reader = await signals.getServiceAttributes({
  name: "signup_returning",
  attribute_key: "domain_userid",
  identifier: domainUserId,
});
// { visits_7d: 4, completed_reads_today: 1 }

if (reader.visits_7d >= 3 && reader.completed_reads_today >= 1) {
  registrationPrompt.show({
    placement: "article_end",
    headline: "You keep coming back. Register and we will keep your place",
  });
}
```

### Rest the prompt for a reader who has closed it twice

This reader has closed the sign-up prompt twice this week and has not signed up. A prompt on a fixed schedule shows it again on the next page, and a third close is the one that becomes a habit. From then on they close it before they read it. A reader who has said no twice gets a rest from the prompt, and when the site asks again, it asks a different way, as a line at the end of the story.

What Signals knows

`prompt_dismissals_7d` 

Times they closed the sign-up prompt in the last seven days

`signups_total` 

Times they have signed up, ever

What your site does

Stops showing the prompt for two weeks once a reader has closed it twice in a week without signing up, and offers the sign-up as a line at the end of the story instead, or not at all.

Define it · PythonUse it · TypeScript

article.tsx · 12 lines Copy Copied 

```
from datetime import timedelta
from snowplow_signals import Attribute, Event, StreamAttributeGroup, domain_userid

prompt_dismissed = Event(name="prompt_dismissed", vendor="com.example", version="1-0-0")
signup_completed = Event(name="signup_completed", vendor="com.example", version="1-0-0")

prompt_dismissals_7d = Attribute(
    name="prompt_dismissals_7d",
    type="int32",
    events=[prompt_dismissed],
    aggregation="counter",
    period=timedelta(days=7),
    default_value=0,
)

signups_total = Attribute(
    name="signups_total",
    type="int32",
    events=[signup_completed],
    aggregation="counter",
    ttl=timedelta(days=365),
    default_value=0,
)

signup_dismissed = StreamAttributeGroup(
    name="signup_dismissed",
    version=1,
    attribute_key=domain_userid,
    owner="you@example.com",
    attributes=[prompt_dismissals_7d, signups_total],
)
```

```
// In the code that renders the article page
const reader = await signals.getServiceAttributes({
  name: "signup_dismissed",
  attribute_key: "domain_userid",
  identifier: domainUserId,
});
// { prompt_dismissals_7d: 2, signups_total: 0 }

if (reader.prompt_dismissals_7d >= 2 && reader.signups_total === 0) {
  signupPrompt.rest({ days: 14 });
  signupPrompt.showInline({ placement: "article_end" });
}
```

## Try it on your own site. 

Add the SDK, define one thing you want to know about a visitor, and watch it update while you click around. The free tier runs on the real engine: no card, no sales call.

[Start free](https://signals.snowplow.io/start) [Talk to an engineer](https://signals.snowplow.io/start?intent=engineer)
